<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html><head>


<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Brizzled: Writing Blogging Software for Google App Engine</title>
<link href="Writing%20Blogging%20Software%20for%20GAE_files/style.css" rel="stylesheet" type="text/css">
<!-- Google Analytics -->

<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script><script src="Writing%20Blogging%20Software%20for%20GAE_files/ga.js" type="text/javascript"></script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-5189686-1");
pageTracker._trackPageview();
</script>

<!-- End Google Analytics -->


</head><body>

<table class="body" border="0" width="100%" cellpadding="0" cellspacing="0">
  <tbody><tr>
    <td colspan="2">&nbsp;</td><td>
  </td></tr>

  <tr>
    <td class="banner" align="left" valign="bottom" width="80%">
      <table border="0" width="100%" cellpadding="0" cellspacing="0">
        <tbody><tr>
          <td align="left">
            <a href="http://brizzled.clapper.org./id/4"><img src="Writing%20Blogging%20Software%20for%20GAE_files/brizzled.png" align="left" border="0" width="341" height="56"></a>
            <div class="blog-abstract">It'll only sting a little.</div>
          </td>
          <td style="padding-right: 1em;" align="right">
            <div class="text-button" style="width: 7em;"><a href="http://brizzled.clapper.org./">top</a></div>
            <div class="text-button" style="width: 7em;"><a href="http://brizzled.clapper.org./archive">everything</a></div>&nbsp;&nbsp;&nbsp;
          </td>
        </tr>
      </tbody></table>
    </td>
    <td align="left" width="20%">
      <div class="gutter">
        <span class="no-comments gutter-item">
          This blog accepts no comments.
          <a href="http://brizzled.clapper.org./id/64">Here's why.</a>
        </span>
      </div>
    </td>
  </tr>

  <tr>
    <td colspan="2">&nbsp;</td><td>
  </td></tr>

  <tr valign="top">
    <td class="body" width="80%">
      <table width="100%" cellpadding="0" cellspacing="0">
        <tbody><tr valign="top">
          <td align="left">
            <div id="articles-container">
            
<table class="first-article-container"><tbody><tr><td>



  <table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody><tr>
      <td><div class="article-title">Writing Blogging Software for Google App Engine</div></td>
      <td><div class="timestamp">6 August, 2008 at 8:11 PM</div></td>
    </tr>
    <tr>
      
      <td colspan="2" class="article-body">
      
      <div class="contents topic" id="contents">
<p class="topic-title first">Contents</p>
<ul class="simple">
<li><a class="reference internal" href="#introduction" id="id4">Introduction</a></li>
<li><a class="reference internal" href="#acknowledgements" id="id5">Acknowledgements</a></li>
<li><a class="reference internal" href="#similar-articles-and-software" id="id6">Similar Articles and Software</a></li>
<li><a class="reference internal" href="#caveats" id="id7">Caveats</a><ul>
<li><a class="reference internal" href="#what-this-blog-engine-supports" id="id8">What this Blog Engine Supports</a></li>
</ul>
</li>
<li><a class="reference internal" href="#the-code" id="id9">The Code</a></li>
<li><a class="reference internal" href="#get-going-with-app-engine" id="id10">Get Going with App Engine</a><ul>
<li><a class="reference internal" href="#register-and-download" id="id11">Register and Download</a></li>
<li><a class="reference internal" href="#create-your-application" id="id12">Create your Application</a></li>
<li><a class="reference internal" href="#open-the-app-engine-docs" id="id13">Open the App Engine Docs</a></li>
<li><a class="reference internal" href="#create-your-local-application-directory" id="id14">Create your Local Application Directory</a></li>
</ul>
</li>
<li><a class="reference internal" href="#create-the-data-model" id="id15">Create the Data Model</a></li>
<li><a class="reference internal" href="#create-the-administration-screens" id="id16">Create the Administration Screens</a><ul>
<li><a class="reference internal" href="#the-templates" id="id17">The Templates</a><ul>
<li><a class="reference internal" href="#main-administration-screen-template" id="id18">Main Administration Screen Template</a></li>
<li><a class="reference internal" href="#the-style-sheet" id="id19">The Style Sheet</a></li>
<li><a class="reference internal" href="#the-edit-screen-template" id="id20">The Edit Screen Template</a></li>
</ul>
</li>
<li><a class="reference internal" href="#the-view-code" id="id21">The View Code</a><ul>
<li><a class="reference internal" href="#defs-py" id="id22"><tt class="docutils literal"><span class="pre">defs.py</span></tt></a></li>
<li><a class="reference internal" href="#request-py" id="id23"><tt class="docutils literal"><span class="pre">request.py</span></tt></a></li>
<li><a class="reference internal" href="#admin-py" id="id24"><tt class="docutils literal"><span class="pre">admin.py</span></tt></a><ul>
<li><a class="reference internal" href="#handlers-that-only-display-a-page" id="id25">Handlers that Only Display a Page</a></li>
<li><a class="reference internal" href="#handlers-that-process-forms" id="id26">Handlers that Process Forms</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><a class="reference internal" href="#the-markup-language" id="id27">The Markup Language</a><ul>
<li><a class="reference internal" href="#docutils" id="id28">Docutils</a></li>
<li><a class="reference internal" href="#translation-code" id="id29">Translation code</a></li>
</ul>
</li>
<li><a class="reference internal" href="#create-the-display-screens" id="id30">Create the Display Screens</a><ul>
<li><a class="reference internal" href="#base-template" id="id31">Base Template</a></li>
<li><a class="reference internal" href="#blog-py" id="id32"><tt class="docutils literal"><span class="pre">blog.py</span></tt></a></li>
<li><a class="reference internal" href="#abstractpagehandler" id="id33"><tt class="docutils literal"><span class="pre">AbstractPageHandler</span></tt></a></li>
<li><a class="reference internal" href="#not-found-page" id="id34">Not Found Page</a></li>
<li><a class="reference internal" href="#main-page" id="id35">Main Page</a></li>
<li><a class="reference internal" href="#show-one-article" id="id36">Show One Article</a></li>
<li><a class="reference internal" href="#show-articles-by-tag" id="id37">Show Articles By Tag</a></li>
<li><a class="reference internal" href="#show-articles-by-month" id="id38">Show Articles By Month</a></li>
<li><a class="reference internal" href="#show-archive" id="id39">Show Archive</a></li>
<li><a class="reference internal" href="#rss-feed" id="id40">RSS Feed</a></li>
</ul>
</li>
<li><a class="reference internal" href="#deploy-the-application" id="id41">Deploy the Application</a></li>
<li><a class="reference internal" href="#handling-static-files-such-as-images" id="id42">Handling Static Files such as Images</a></li>
<li><a class="reference internal" href="#previewing-a-draft" id="id43">Previewing a Draft</a></li>
<li><a class="reference internal" href="#enhancements" id="id44">Enhancements</a></li>
<li><a class="reference internal" href="#in-closing" id="id45">In Closing</a></li>
<li><a class="reference internal" href="#feedback" id="id46">Feedback</a></li>
<li><a class="reference internal" href="#follow-up-articles" id="id47">Follow-up Articles</a></li>
</ul>
</div>
<div class="section" id="introduction">
<h1><a class="toc-backref" href="#id4">Introduction</a></h1>
<p>As noted previously, I recently <a class="reference external" href="http://brizzled.clapper.org./id/76">rehosted this blog</a> on Google's <a class="reference external" href="http://appengine.google.com/">App
Engine</a> (GAE). Writing a small, but functional, blog engine is a useful
exercise in learning a web framework. I wrote blog software to learn
more about <a class="reference external" href="http://www.djangoproject.com/">Django</a>, and I ported it to App Engine to learn more about
App Engine.</p>
<p>In this article, I recreate the steps necessary to build a blogging engine that runs under GAE.</p>
</div>
<div class="section" id="acknowledgements">
<h1><a class="toc-backref" href="#id5">Acknowledgements</a></h1>
<p>I'm grateful to the following people for reviewing and commenting on this
article before I published it.</p>
<ul class="simple">
<li><a class="reference external" href="http://blog.geewax.org/">J.J. Geewax</a></li>
<li><a class="reference external" href="http://blog.cbcg.net/">Toby DiPasquale</a></li>
<li><a class="reference external" href="http://hipstersinc.com/">Mark Chadwick</a></li>
<li><a class="reference external" href="http://www.row-5.com/">Rob Keiser</a></li>
</ul>
<p>In addition, the following people sent me some valuable insights and
corrections after the article was published:</p>
<ul class="simple">
<li><a class="reference external" href="http://ww.billkatz.com/">Bill Katz</a>, for clarifying the querying of list properties.</li>
<li><a class="reference external" href="http://fernandoacorreia.wordpress.com/">Fernando Correia</a>, for reminding me that unique keys do have associated unique IDs, forcing me to re-read that part of the GAE docs again.</li>
</ul>
</div>
<div class="section" id="similar-articles-and-software">
<h1><a class="toc-backref" href="#id6">Similar Articles and Software</a></h1>
<ul class="simple">
<li><a class="reference external" href="http://bret.appspot.com/entry/experimenting-google-app-engine">Experimenting with Google App Engine</a>, by Bret Taylor. Also describes
building a blogging engine on GAE.</li>
<li><a class="reference external" href="http://bloog.billkatz.com/">Bloog</a>, Bill Katz's RESTful GAE blogging engine.</li>
<li><a class="reference external" href="http://code.google.com/p/cpedialog/">cpedialog</a>, another GAE-based blogging engine.</li>
</ul>
</div>
<div class="section" id="caveats">
<h1><a class="toc-backref" href="#id7">Caveats</a></h1>
<p>Before I jump into the tutorial, there are a few caveats:</p>
<ul class="simple">
<li>The point of this article is to build an App Engine application, to get
to know some GAE internals. If you just want to host a blog on GAE, and
you're not interested in understanding the software involved, you might
consider installing <a class="reference external" href="http://code.google.com/p/cpedialog/">cpedialog</a>, a blogging engine that will run on GAE.</li>
<li>I am <em>certain</em> there are things about GAE that I could do better. I
welcome corrections and suggestions; just drop me an email.</li>
</ul>
<div class="section" id="what-this-blog-engine-supports">
<h2><a class="toc-backref" href="#id8">What this Blog Engine Supports</a></h2>
<p>The blog engine outlined in this article is fully functional; this blog
runs on similar software. However, it lacks a few features some people
might want, such as:</p>
<ul class="simple">
<li>Image uploading. I just haven't put that in yet. When I do, I'll update
this article. In the meantime, I'm able to live without it by uploading
the images via GAE's <tt class="docutils literal"><span class="pre">appcfg.py</span> <span class="pre">update</span></tt> capability.</li>
<li>Comments (because I <a class="reference external" href="http://brizzled.clapper.org./id/64">don't currently support comments</a>). I may add
commenting back to my blog, however, since GAE makes it easy to accept
comments only from registered Google users, which might cut down on the
blog spam.</li>
<li>Integration into blog aggregators like <a class="reference external" href="http://www.technorati.com/">Technorati</a>.</li>
</ul>
<p>It <em>does</em> have the following features, though:</p>
<ul class="simple">
<li>Tag handling, including support for generating a tag cloud.</li>
<li>Support for RSS and Atom feeds.</li>
<li>Displaying articles by month or tag.</li>
<li>Template-driven theme customization.</li>
<li>Unpublished drafts.</li>
<li>Secured administration screens.</li>
<li><a class="reference external" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> markup (instead of HTML) for the articles.</li>
</ul>
<p>In short, it's a serviceable blog engine, with simple, straightforward code
you can customize as you see fit.</p>
</div>
</div>
<div class="section" id="the-code">
<h1><a class="toc-backref" href="#id9">The Code</a></h1>
<p>The source code for this blog engine is available at
<a class="reference external" href="http://www.clapper.org/software/python/picoblog/">http://www.clapper.org/software/python/picoblog/</a></p>
</div>
<div class="section" id="get-going-with-app-engine">
<h1><a class="toc-backref" href="#id10">Get Going with App Engine</a></h1>
<div class="section" id="register-and-download">
<h2><a class="toc-backref" href="#id11">Register and Download</a></h2>
<p>First, of course, you have to register with <a class="reference external" href="http://appengine.google.com/">GAE</a> and download the
development kit. This article assumes you've already done that.</p>
</div>
<div class="section" id="create-your-application">
<h2><a class="toc-backref" href="#id12">Create your Application</a></h2>
<p>Next, from your GAE account, create a new application. You'll have to
create a unique identifier for the application. In this article, I use the
application ID "picoblog". You'll want to use something else.</p>
<p>This article is not a tutorial on how to use the App Engine tools and web
site; it's an article about building a blog application. So, I'm not to go
into details about how to create your application on the <a class="reference external" href="http://appengine.google.com/">GAE</a> site.
Google's wizard is easy enough to follow.</p>
</div>
<div class="section" id="open-the-app-engine-docs">
<h2><a class="toc-backref" href="#id13">Open the App Engine Docs</a></h2>
<p>You'll want to have the online <a class="reference external" href="http://code.google.com/appengine/docs/">App Engine documentation</a> available as
you develop your App Engine application. It wouldn't hurt to review the
<em>Getting Starting</em> section before jumping in.</p>
</div>
<div class="section" id="create-your-local-application-directory">
<h2><a class="toc-backref" href="#id14">Create your Local Application Directory</a></h2>
<p id="app-yaml">Create a directory called <tt class="docutils literal"><span class="pre">picoblog</span></tt> in which to do your work.
In that directory, create a file called <tt class="docutils literal"><span class="pre">app.yaml</span></tt>:</p>
<div class="code-block text">
<div class="highlight"><pre>application: picoblog
version: 1
runtime: python
api_version: 1

handlers:
- url: /static
  static_dir: static

- url: /favicon.ico
  static_files: static/favicon.ico
  upload: static/favicon.ico

- url: /admin
  script: admin.py
  login: admin

- url: /admin/.*
  script: admin.py
  login: admin

- url: /.*
  script: blog.py
</pre></div>

</div>
<p>This file configures your application. You can treat most of the top of the
file as magic. For now, the parts we care about are:</p>
<table class="docutils field-list" frame="void" rules="none">
<col class="field-name">
<col class="field-body">
<tbody valign="top">
<tr class="field"><th class="field-name"><tt class="docutils literal"><span class="pre">application</span></tt>:</th><td class="field-body"><p class="first">The application's registered ID is "picoblog".</p>
</td>
</tr>
<tr class="field"><th class="field-name"><tt class="docutils literal"><span class="pre">handlers</span></tt>:</th><td class="field-body"><p class="first">Each <tt class="docutils literal"><span class="pre">url</span></tt> entry is a regular expression GAE will match
against incoming URL requests; on a match, it'll run the associated
script. In this case, we're saying:</p>
<ul class="last simple">
<li>Any path starting with <tt class="docutils literal"><span class="pre">/static</span></tt> is resolved via the built-in
static file handler. This is where we'll put our images. So go ahead
and create a <tt class="docutils literal"><span class="pre">static</span></tt> directory under <tt class="docutils literal"><span class="pre">picoblog</span></tt>.</li>
<li>Since browsers always look for <tt class="docutils literal"><span class="pre">/favicon.ico</span></tt>, and I get tired of
seeing all the "not found" messages in the logs, there's an entry
for an icon. It's stored in the <tt class="docutils literal"><span class="pre">static</span></tt> directory.</li>
<li>The administrative screens (for creating and editing blog articles)
live under '/admin' and are secured: Only a GMail account with
administrative privileges on the project is allowed to get to them.
They're handled by the <tt class="docutils literal"><span class="pre">admin.py</span></tt> script. We'll be creating that
script in the <tt class="docutils literal"><span class="pre">picoblog</span></tt> directory.</li>
<li>Finally, the published blog itself matches everything else, and it's
handled by the <tt class="docutils literal"><span class="pre">blog.py</span></tt> script. That script, too, will end up
in the <tt class="docutils literal"><span class="pre">picoblog</span></tt> directory.</li>
</ul>
</td>
</tr>
</tbody>
</table>
<p>Once you've created <tt class="docutils literal"><span class="pre">app.yaml</span></tt>, you can pretty much forget about it for
awhile.</p>
</div>
</div>
<div class="section" id="create-the-data-model">
<h1><a class="toc-backref" href="#id15">Create the Data Model</a></h1>
<p>The next step is to decide what data we're storing in the database. For
this blog engine, there's a single object type in the database, called
an <tt class="docutils literal"><span class="pre">Article</span></tt>. It has the following properties (which would be columns in
a SQL database):</p>
<ul>
<li><p class="first"><tt class="docutils literal"><span class="pre">title</span></tt>: The 1-line title of the article</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">body</span></tt>: The body of the article, which is reStructuredText markup.</p>
</li>
<li><dl class="first docutils">
<dt><tt class="docutils literal"><span class="pre">draft</span></tt>: Whether or not the article is a draft (i.e., not published)</dt>
<dd><p class="first last">or not. Drafts are only visible in the administration screen.</p>
</dd>
</dl>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">published_when</span></tt>: The time the entry was published. In this context,
"published" means "goes from being a draft to not being a draft". This time
stamp is initialized to the time the article is created, and it's updated
when the article is saved as a non-draft. (Toggling the "draft" flag
multiple times will continue to update this time; you're obviously
free to change that behavior by hacking the code.)</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">tags</span></tt>: a list of tags (strings) assigned to the articles. May be empty.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">id</span></tt>: a unique ID assigned to the article.</p>
</li>
</ul>
<p>A note about the unique ID: GAE does not provide support for an
automatically incremented integer ID field the same way that Django does.
An item in the datastore <em>does</em> have a unique key, accessible via the
<tt class="docutils literal"><span class="pre">key()</span></tt> method. Further, that key can be converted to a corresponding
unique name or number (depending on how the key was assigned) by calling
<tt class="docutils literal"><span class="pre">key().id()</span></tt>. For instance:</p>
<div class="code-block python">
<div class="highlight"><pre><span class="n">article</span> <span class="o">=</span> <span class="n">Article</span><span class="p">(</span><span class="n">title</span><span class="o">=</span><span class="s">'Test title'</span><span class="p">)</span>
<span class="n">article</span><span class="o">.</span><span class="n">put</span><span class="p">()</span>
<span class="k">print</span> <span class="n">article</span><span class="o">.</span><span class="n">key</span><span class="p">()</span><span class="o">.</span><span class="n">id</span><span class="p">()</span>
</pre></div>

</div>
<p>However, you cannot use this ID in a query. Quoting from the
<a class="reference external" href="http://code.google.com/appengine/docs/datastore/keysandentitygroups.html">Keys and Entity Groups</a> section of the GAE documentation:</p>
<blockquote>
Key names and IDs cannot be used like property values in queries.
However, you can use a named key, then store the name as a property.
You could do something similar with numeric IDs by storing the object
to assign the ID, getting the ID value using obj.key().id(), setting
the property with the ID, then storing the object again.</blockquote>
<p>So, that's what we're going to do.</p>
<p>The data model for our <tt class="docutils literal"><span class="pre">Article</span></tt> class looks like this:</p>
<div class="code-block python">
<div class="highlight"><pre><span class="k">import</span> <span class="nn">datetime</span>
<span class="k">import</span> <span class="nn">sys</span>

<span class="k">from</span> <span class="nn">google.appengine.ext</span> <span class="k">import</span> <span class="n">db</span>

<span class="k">class</span> <span class="nc">Article</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>

    <span class="n">title</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">StringProperty</span><span class="p">(</span><span class="n">required</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
    <span class="n">body</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">TextProperty</span><span class="p">()</span>
    <span class="n">published_when</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">DateTimeProperty</span><span class="p">(</span><span class="n">auto_now_add</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
    <span class="n">tags</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">ListProperty</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Category</span><span class="p">)</span>
    <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">StringProperty</span><span class="p">()</span>
    <span class="n">draft</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">BooleanProperty</span><span class="p">(</span><span class="n">required</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
</pre></div>

</div>
<p>If you're familiar with Django, you'll notice that it's similar to Django's
data models, but not exactly the same.</p>
<p>Next, since I like to hide as much of the database API semantics inside
the model, I'm going to add a <tt class="docutils literal"><span class="pre">get_all()</span></tt> method that returns all
articles, a <tt class="docutils literal"><span class="pre">get()</span></tt> method that returns a single article by ID, and
a <tt class="docutils literal"><span class="pre">published()</span></tt> method that returns all non-draft articles. (The
<tt class="docutils literal"><span class="pre">published()</span></tt> method will be separated into two methods, so the query
itself can be shared. More on that later.)</p>
<div class="code-block python">
<div class="highlight"><pre><span class="nd">@classmethod</span>
<span class="k">def</span> <span class="nf">get_all</span><span class="p">(</span><span class="n">cls</span><span class="p">):</span>
    <span class="n">q</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Query</span><span class="p">(</span><span class="n">Article</span><span class="p">)</span>
    <span class="n">q</span><span class="o">.</span><span class="n">order</span><span class="p">(</span><span class="s">'-published_when'</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">q</span><span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="n">FETCH_THEM_ALL</span><span class="p">)</span>

<span class="nd">@classmethod</span>
<span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="nb">id</span><span class="p">):</span>
    <span class="n">q</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Query</span><span class="p">(</span><span class="n">Article</span><span class="p">)</span>
    <span class="n">q</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="s">'id = '</span><span class="p">,</span> <span class="nb">id</span><span class="p">)</span>
    <span class="n">articles</span> <span class="o">=</span> <span class="n">q</span><span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="mf">1</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">articles</span><span class="p">[</span><span class="mf">0</span><span class="p">]</span> <span class="k">if</span> <span class="n">articles</span> <span class="k">else</span> <span class="bp">None</span>

<span class="nd">@classmethod</span>
<span class="k">def</span> <span class="nf">published_query</span><span class="p">(</span><span class="n">cls</span><span class="p">):</span>
    <span class="n">q</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Query</span><span class="p">(</span><span class="n">Article</span><span class="p">)</span>
    <span class="n">q</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="s">'draft = '</span><span class="p">,</span> <span class="bp">False</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">q</span>

<span class="nd">@classmethod</span>
<span class="k">def</span> <span class="nf">published</span><span class="p">(</span><span class="n">cls</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">Article</span><span class="o">.</span><span class="n">published_query</span><span class="p">()</span>\
                  <span class="o">.</span><span class="n">order</span><span class="p">(</span><span class="s">'-published_when'</span><span class="p">)</span>\
                  <span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="n">FETCH_THEM_ALL</span><span class="p">)</span>
</pre></div>

</div>
<p><tt class="docutils literal"><span class="pre">FETCH_THEM_ALL</span></tt> is an integer constant with a large value, defined at the
top of the module.</p>
<p>For a more complete understanding of the GAE query interface, see the
<a class="reference external" href="http://code.google.com/appengine/docs/datastore/queryclass.html">documentation for the Query class</a> and the
<a class="reference external" href="http://code.google.com/appengine/docs/datastore/queryclass.html#Query_filter">GAE Query Filter</a> documentation.</p>
<p>Finally, let's add a <tt class="docutils literal"><span class="pre">save()</span></tt> method that does two important things:</p>
<ul class="simple">
<li>Copies the GAE-assigned unique ID into our <tt class="docutils literal"><span class="pre">id</span></tt> field, so we can use it
in queries.</li>
<li>Updates the time stamp if the article being saved is going from draft to
published status.</li>
</ul>
<div class="code-block python">
<div class="highlight"><pre><span class="k">def</span> <span class="nf">save</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
    <span class="n">previous_version</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">draft</span> <span class="o">=</span> <span class="n">previous_version</span><span class="o">.</span><span class="n">draft</span>
    <span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
        <span class="n">draft</span> <span class="o">=</span> <span class="bp">False</span>

    <span class="k">if</span> <span class="n">draft</span> <span class="ow">and</span> <span class="p">(</span><span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">draft</span><span class="p">):</span>
        <span class="c"># Going from draft to published. Update the timestamp.</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">published_when</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span>

    <span class="k">try</span><span class="p">:</span>
        <span class="n">obj_id</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="p">()</span><span class="o">.</span><span class="n">id</span><span class="p">()</span>
        <span class="n">resave</span> <span class="o">=</span> <span class="bp">False</span>
    <span class="k">except</span> <span class="n">db</span><span class="o">.</span><span class="n">NotSavedError</span><span class="p">:</span>
        <span class="c"># No key, hence no ID yet. This one hasn't been saved.</span>
        <span class="c"># We'll save it once without the ID field; this first</span>
        <span class="c"># save will cause GAE to assign it a key. Then, we can</span>
        <span class="c"># extract the ID, put it in our ID field, and resave</span>
        <span class="c"># the object.</span>
        <span class="n">resave</span> <span class="o">=</span> <span class="bp">True</span>

    <span class="bp">self</span><span class="o">.</span><span class="n">put</span><span class="p">()</span>
    <span class="k">if</span> <span class="n">resave</span><span class="p">:</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">id</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="p">()</span><span class="o">.</span><span class="n">id</span><span class="p">()</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">put</span><span class="p">()</span>
</pre></div>

</div>
<p>Okay, that's the model. (See the <a class="reference external" href="http://www.clapper.org/software/python/picoblog/">source code</a> for the complete file.)</p>
</div>
<div class="section" id="create-the-administration-screens">
<h1><a class="toc-backref" href="#id16">Create the Administration Screens</a></h1>
<p>Now let's create the administration screens, so we can edit and create articles.
We'll create two screens.</p>
<p>The main administration screen contains three things:</p>
<ul class="simple">
<li>A button to create a new article. Pressing this button creates an empty
article and launches the Edit Article screen to edit it.</li>
<li>A button to go back to blog itself.</li>
<li>A list of the existing articles. The articles will be sorted in reverse
chronological order, and each article's date and title will be displayed.
The article's date and title will also be a hyperlink to the edit screen
for the article. Further, drafts will be shown in red, to distinguish them
from published articles.</li>
</ul>
<p>The Edit Article screen contains</p>
<ul class="simple">
<li>A text box for the the title</li>
<li>A text area for the body of the article, which is assumed to be reStructuredText</li>
<li>A text box for the list of tags (comma-separated)</li>
<li>A check box to indicate whether or not the item is a draft</li>
</ul>
<p>To create these screens requires five files:</p>
<ul class="simple">
<li><tt class="docutils literal"><span class="pre">defs.py</span></tt> will hold some constants that we share between all the blog scripts.</li>
<li><tt class="docutils literal"><span class="pre">request.py</span></tt> will hold a base class request handler, which is basically a place to hang logic that we need in every script.</li>
<li><tt class="docutils literal"><span class="pre">admin.py</span></tt> contains the Python code for the admin screens--the equivalent of a Django <tt class="docutils literal"><span class="pre">views.py</span></tt> file for the admin screns.</li>
<li><tt class="docutils literal"><span class="pre">admin-main.html</span></tt> is the template for the main administration screen.</li>
<li><tt class="docutils literal"><span class="pre">admin-edit.html</span></tt> is the template for the Edit Article screen.</li>
</ul>
<p>To keep things organized, we'll store the templates in a <tt class="docutils literal"><span class="pre">templates</span></tt>
subdirectory.</p>
<div class="section" id="the-templates">
<h2><a class="toc-backref" href="#id17">The Templates</a></h2>
<p>Let's start with the templates.</p>
<p>GAE's default template engine is Django's template engine. If you don't
know Django's template language, read the first few sections of the
<a class="reference external" href="http://www.djangoproject.com/documentation/0.96/templates/">Django template language</a> document. Describing Django templates is beyond
the scope of this article.</p>
<div class="section" id="main-administration-screen-template">
<h3><a class="toc-backref" href="#id18">Main Administration Screen Template</a></h3>
<p>You can see the <a class="reference external" href="http://brizzled.clapper.org./static/gae/admin-main.txt">full template for the main administration screen here</a>.</p>
<p>It consists of a link to the style sheet, some Javascript, some standard HTML
layout, and this block of template logic:</p>
<div class="code-block html">
<div class="highlight"><pre>&lt;ul&gt;
{% for article in articles %}
  {% if article.draft %}
  &lt;li class="admin-draft"&gt;
  {% else %}
  &lt;li class="admin-published"&gt;
  {% endif %}
  {{ article.published_when|date:"j F, Y" }}&amp;nbsp;
  &lt;a href="/admin/article/edit/?id={{ article.id }}"&gt;{{ article.title }}&lt;/a&gt;
{% endfor %}
&lt;/ul&gt;
</pre></div>

</div>
<p>This template code assumes that the variables passed to the template will
include a Python list called <tt class="docutils literal"><span class="pre">articles</span></tt>, each element of which is an
<tt class="docutils literal"><span class="pre">Article</span></tt> object. We'll see how that's populated in the next section.</p>
<p>The style sheet link looks like this:</p>
<div class="code-block html">
<div class="highlight"><pre>&lt;link href="/static/style.css" rel="stylesheet" type="text/css"/&gt;
</pre></div>

</div>
<p>Rather than use a template <tt class="docutils literal"><span class="pre">{%</span> <span class="pre">include</span> <span class="pre">"style.css"</span> <span class="pre">%}</span></tt> directive to pull
the style sheet file inline at rendering time, we're telling the browser to
go get it. We'll be using the same style sheet for all pages; using an
external style sheet allows the browser to cache it.</p>
</div>
<div class="section" id="the-style-sheet">
<h3><a class="toc-backref" href="#id19">The Style Sheet</a></h3>
<p>To see the style sheet, <a class="reference external" href="http://brizzled.clapper.org./static/gae/style.css">follow this link</a>.  The style sheet is stored in
the <tt class="docutils literal"><span class="pre">static</span></tt> subdirectory, where it'll be served by the GAE static file
handler.</p>
</div>
<div class="section" id="the-edit-screen-template">
<h3><a class="toc-backref" href="#id20">The Edit Screen Template</a></h3>
<p>The <a class="reference external" href="http://brizzled.clapper.org./static/gae/admin-edit.txt">template for the edit screen is available here</a>. The edit screen is
slightly more complicated, since it has some Javascript to handle the
various buttons. But overall, it's still pretty simple as web screens go.</p>
</div>
</div>
<div class="section" id="the-view-code">
<h2><a class="toc-backref" href="#id21">The View Code</a></h2>
<p>The view code for the administration screens is in <tt class="docutils literal"><span class="pre">admin.py</span></tt>. It, too, is
relatively simple. But first, let's look at the two other files we're using
to consolidate common logic.</p>
<div class="section" id="defs-py">
<h3><a class="toc-backref" href="#id22"><tt class="docutils literal"><span class="pre">defs.py</span></tt></a></h3>
<p><tt class="docutils literal"><span class="pre">defs.py</span></tt> just contains some common constants:</p>
<div class="code-block python">
<div class="highlight"><pre><span class="n">BLOG_NAME</span> <span class="o">=</span> <span class="s">'PicoBlog'</span>
<span class="n">BLOG_OWNER</span> <span class="o">=</span> <span class="s">'Joe Example'</span>

<span class="n">TEMPLATE_SUBDIR</span> <span class="o">=</span> <span class="s">'templates'</span>

<span class="n">TAG_URL_PATH</span> <span class="o">=</span> <span class="s">'tag'</span>
<span class="n">DATE_URL_PATH</span> <span class="o">=</span> <span class="s">'date'</span>
<span class="n">ARTICLE_URL_PATH</span> <span class="o">=</span> <span class="s">'id'</span>
<span class="n">MEDIA_URL_PATH</span> <span class="o">=</span> <span class="s">'static'</span>
<span class="n">ATOM_URL_PATH</span> <span class="o">=</span> <span class="s">'atom'</span>
<span class="n">RSS2_URL_PATH</span> <span class="o">=</span> <span class="s">'rss2'</span>
<span class="n">ARCHIVE_URL_PATH</span> <span class="o">=</span> <span class="s">'archive'</span>

<span class="n">MAX_ARTICLES_PER_PAGE</span> <span class="o">=</span> <span class="mf">5</span>
<span class="n">TOTAL_RECENT</span> <span class="o">=</span> <span class="mf">10</span>
</pre></div>

</div>
<p>We'll see how they're used as we get further into this tutorial.</p>
</div>
<div class="section" id="request-py">
<h3><a class="toc-backref" href="#id23"><tt class="docutils literal"><span class="pre">request.py</span></tt></a></h3>
<p><tt class="docutils literal"><span class="pre">request.py</span></tt> contains our base request handler class:</p>
<div class="code-block python">
<div class="highlight"><pre><span class="k">import</span> <span class="nn">os</span>

<span class="k">from</span> <span class="nn">google.appengine.ext</span> <span class="k">import</span> <span class="n">webapp</span>
<span class="k">from</span> <span class="nn">google.appengine.ext.webapp</span> <span class="k">import</span> <span class="n">template</span>

<span class="k">import</span> <span class="nn">defs</span>

<span class="k">class</span> <span class="nc">BlogRequestHandler</span><span class="p">(</span><span class="n">webapp</span><span class="o">.</span><span class="n">RequestHandler</span><span class="p">):</span>
    <span class="sd">"""</span>
<span class="sd">    Base class for all request handlers in this application. This class</span>
<span class="sd">    serves primarily to isolate common logic.</span>
<span class="sd">    """</span>

    <span class="k">def</span> <span class="nf">get_template</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">template_name</span><span class="p">):</span>
        <span class="sd">"""</span>
<span class="sd">        Return the full path of the template.</span>

<span class="sd">        :Parameters:</span>
<span class="sd">            template_name : str</span>
<span class="sd">                Simple name of the template</span>

<span class="sd">        :rtype: str</span>
<span class="sd">        :return: the full path to the template. Does *not* ensure that the</span>
<span class="sd">                 template exists.</span>
<span class="sd">        """</span>
        <span class="k">return</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="n">__file__</span><span class="p">),</span>
                            <span class="n">defs</span><span class="o">.</span><span class="n">TEMPLATE_SUBDIR</span><span class="p">,</span>
                            <span class="n">template_name</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">render_template</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">template_name</span><span class="p">,</span> <span class="n">template_vars</span><span class="p">):</span>
        <span class="sd">"""</span>
<span class="sd">        Render a template and write the output to ``self.response.out``.</span>

<span class="sd">        :Parameters:</span>
<span class="sd">            template_name : str</span>
<span class="sd">                Simple name of the template</span>

<span class="sd">            template_vars : dict</span>
<span class="sd">                Dictionary of variables to make available to the template.</span>
<span class="sd">                Can be empty.</span>
<span class="sd">        """</span>
        <span class="n">template_path</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_template</span><span class="p">(</span><span class="n">template_name</span><span class="p">)</span>
        <span class="n">template</span><span class="o">.</span><span class="n">render</span><span class="p">(</span><span class="n">template_path</span><span class="p">,</span> <span class="n">template_vars</span><span class="p">)</span>
</pre></div>

</div>
<p>As you can see, it just contains some methods to make rendering templates
a little simpler.</p>
</div>
<div class="section" id="admin-py">
<h3><a class="toc-backref" href="#id24"><tt class="docutils literal"><span class="pre">admin.py</span></tt></a></h3>
<p><em>Now</em> we're ready to look at the administration view code. First, we have
some imports:</p>
<div class="code-block python">
<div class="highlight"><pre><span class="k">import</span> <span class="nn">cgi</span>

<span class="k">from</span> <span class="nn">google.appengine.api</span> <span class="k">import</span> <span class="n">users</span>
<span class="k">from</span> <span class="nn">google.appengine.ext</span> <span class="k">import</span> <span class="n">webapp</span>
<span class="k">from</span> <span class="nn">google.appengine.ext.webapp</span> <span class="k">import</span> <span class="n">util</span>

<span class="k">from</span> <span class="nn">models</span> <span class="k">import</span> <span class="o">*</span>
<span class="k">import</span> <span class="nn">request</span>
</pre></div>

</div>
<p>These are followed by the request handler classes:</p>
<div class="code-block python">
<div class="highlight"><pre><span class="k">class</span> <span class="nc">ShowArticlesHandler</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">BlogRequestHandler</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">articles</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">get_all</span><span class="p">()</span>
        <span class="n">template_vars</span> <span class="o">=</span> <span class="p">{</span><span class="s">'articles'</span> <span class="p">:</span> <span class="n">articles</span><span class="p">}</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">response</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">render_template</span><span class="p">(</span><span class="s">'admin-main.html'</span><span class="p">,</span>
                                                     <span class="n">template_vars</span><span class="p">))</span>

<span class="k">class</span> <span class="nc">NewArticleHandler</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">BlogRequestHandler</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">article</span> <span class="o">=</span> <span class="n">Article</span><span class="p">(</span><span class="n">title</span><span class="o">=</span><span class="s">'New article'</span><span class="p">,</span>
                          <span class="n">body</span><span class="o">=</span><span class="s">'Content goes here'</span><span class="p">,</span>
                          <span class="n">draft</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
        <span class="n">template_vars</span> <span class="o">=</span> <span class="p">{</span><span class="s">'article'</span> <span class="p">:</span> <span class="n">article</span><span class="p">}</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">response</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">render_template</span><span class="p">(</span><span class="s">'admin-edit.html'</span><span class="p">,</span>
                                                     <span class="n">template_vars</span><span class="p">))</span>

<span class="k">class</span> <span class="nc">SaveArticleHandler</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">BlogRequestHandler</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">post</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">title</span> <span class="o">=</span> <span class="n">cgi</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'title'</span><span class="p">))</span>
        <span class="n">body</span> <span class="o">=</span> <span class="n">cgi</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'content'</span><span class="p">))</span>
        <span class="nb">id</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">cgi</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'id'</span><span class="p">)))</span>
        <span class="n">tags</span> <span class="o">=</span> <span class="n">cgi</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'tags'</span><span class="p">))</span>
        <span class="n">published_when</span> <span class="o">=</span> <span class="n">cgi</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'published_when'</span><span class="p">))</span>
        <span class="n">draft</span> <span class="o">=</span> <span class="n">cgi</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'draft'</span><span class="p">))</span>
        <span class="k">if</span> <span class="n">tags</span><span class="p">:</span>
            <span class="n">tags</span> <span class="o">=</span> <span class="p">[</span><span class="n">t</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">tags</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s">','</span><span class="p">)]</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">tags</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="n">tags</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">convert_string_tags</span><span class="p">(</span><span class="n">tags</span><span class="p">)</span>

        <span class="k">if</span> <span class="ow">not</span> <span class="n">draft</span><span class="p">:</span>
            <span class="n">draft</span> <span class="o">=</span> <span class="bp">False</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">draft</span> <span class="o">=</span> <span class="p">(</span><span class="n">draft</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="o">==</span> <span class="s">'on'</span><span class="p">)</span>

        <span class="n">article</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">article</span><span class="p">:</span>
            <span class="c"># It's an edit of an existing item.</span>
            <span class="n">article</span><span class="o">.</span><span class="n">title</span> <span class="o">=</span> <span class="n">title</span>
            <span class="n">article</span><span class="o">.</span><span class="n">body</span> <span class="o">=</span> <span class="n">body</span>
            <span class="n">article</span><span class="o">.</span><span class="n">tags</span> <span class="o">=</span> <span class="n">tags</span>

            <span class="n">article</span><span class="o">.</span><span class="n">draft</span> <span class="o">=</span> <span class="n">draft</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="c"># It's new.</span>
            <span class="n">article</span> <span class="o">=</span> <span class="n">Article</span><span class="p">(</span><span class="n">title</span><span class="o">=</span><span class="n">title</span><span class="p">,</span>
                              <span class="n">body</span><span class="o">=</span><span class="n">body</span><span class="p">,</span>
                              <span class="n">tags</span><span class="o">=</span><span class="n">tags</span><span class="p">,</span>
                              <span class="nb">id</span><span class="o">=</span><span class="nb">id</span><span class="p">,</span>
                              <span class="n">draft</span><span class="o">=</span><span class="n">draft</span><span class="p">)</span>

        <span class="n">article</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>

        <span class="n">edit_again</span> <span class="o">=</span> <span class="n">cgi</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'edit_again'</span><span class="p">))</span>
        <span class="n">edit_again</span> <span class="o">=</span> <span class="n">edit_again</span> <span class="ow">and</span> <span class="p">(</span><span class="n">edit_again</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="o">==</span> <span class="s">'true'</span><span class="p">)</span>

        <span class="k">if</span> <span class="n">edit_again</span><span class="p">:</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">redirect</span><span class="p">(</span><span class="s">'/admin/article/edit/?id=</span><span class="si">%s</span><span class="s">'</span> <span class="o">%</span> <span class="nb">id</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">redirect</span><span class="p">(</span><span class="s">'/admin/'</span><span class="p">)</span>

<span class="k">class</span> <span class="nc">EditArticleHandler</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">BlogRequestHandler</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="nb">id</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'id'</span><span class="p">))</span>
        <span class="n">article</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">article</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">,</span> <span class="s">'Article with ID </span><span class="si">%d</span><span class="s"> does not exist.'</span> <span class="o">%</span> <span class="nb">id</span>

        <span class="n">article</span><span class="o">.</span><span class="n">tag_string</span> <span class="o">=</span> <span class="s">', '</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">article</span><span class="o">.</span><span class="n">tags</span><span class="p">)</span>
        <span class="n">template_vars</span> <span class="o">=</span> <span class="p">{</span><span class="s">'article'</span>  <span class="p">:</span> <span class="n">article</span><span class="p">}</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">response</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">render_template</span><span class="p">(</span><span class="s">'admin-edit.html'</span><span class="p">,</span>
                                <span class="n">template_vars</span><span class="p">))</span>

<span class="k">class</span> <span class="nc">DeleteArticleHandler</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">BlogRequestHandler</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="nb">id</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'id'</span><span class="p">))</span>
        <span class="n">article</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">article</span><span class="p">:</span>
            <span class="n">article</span><span class="o">.</span><span class="n">delete</span><span class="p">()</span>

        <span class="bp">self</span><span class="o">.</span><span class="n">redirect</span><span class="p">(</span><span class="s">'/admin/'</span><span class="p">)</span>
</pre></div>

</div>
<p>The file ends with some initialization logic and the main program:</p>
<div class="code-block python">
<div class="highlight"><pre><span class="n">application</span> <span class="o">=</span> <span class="n">webapp</span><span class="o">.</span><span class="n">WSGIApplication</span><span class="p">(</span>
    <span class="p">[(</span><span class="s">'/admin/?'</span><span class="p">,</span> <span class="n">ShowArticlesHandler</span><span class="p">),</span>
     <span class="p">(</span><span class="s">'/admin/article/new/?'</span><span class="p">,</span> <span class="n">NewArticleHandler</span><span class="p">),</span>
     <span class="p">(</span><span class="s">'/admin/article/delete/?'</span><span class="p">,</span> <span class="n">DeleteArticleHandler</span><span class="p">),</span>
     <span class="p">(</span><span class="s">'/admin/article/save/?'</span><span class="p">,</span> <span class="n">SaveArticleHandler</span><span class="p">),</span>
     <span class="p">(</span><span class="s">'/admin/article/edit/?'</span><span class="p">,</span> <span class="n">EditArticleHandler</span><span class="p">),</span>
     <span class="p">],</span>

    <span class="n">debug</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
    <span class="n">util</span><span class="o">.</span><span class="n">run_wsgi_app</span><span class="p">(</span><span class="n">application</span><span class="p">)</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
    <span class="n">main</span><span class="p">()</span>
</pre></div>

</div>
<p>Let's break this down a bit. First, the initialization logic at the bottom
(that is, the creation of the <tt class="docutils literal"><span class="pre">webapp.WSGIApplication</span></tt> object) defines
what classes (<em>handlers</em>) will handle which URLs within the <tt class="docutils literal"><span class="pre">/admin/</span></tt> URL
space. Recall that the <a class="reference internal" href="#app-yaml">app.yaml</a> file points all <tt class="docutils literal"><span class="pre">/admin</span></tt> URLs to this
file. The <tt class="docutils literal"><span class="pre">application</span></tt> variable further breaks those URLs down, so that
certain URLs map to certain handlers. The list passed to the
<tt class="docutils literal"><span class="pre">WSGIApplication</span></tt> constructor contains tuples; each tuple defines a
URL mapping.</p>
<ul class="simple">
<li>The first element of the tuple is a regular expression. Note that the
regular expressions we're using end with <tt class="docutils literal"><span class="pre">/?</span></tt>, allowing the trailing
slash to be omitted in the URL.</li>
<li>The name of the class that will handle requests to URLs that match the
regular expression.</li>
</ul>
<div class="note">
<p class="first admonition-title">Note</p>
<p class="last">The regular expressions can contain groups, which are then passed
as arguments to the corresponding request handler. We'll see examples of
that later.</p>
</div>
<p>Next, let's look at some of the handlers. There are basically two kinds of
handlers here:</p>
<ul class="simple">
<li>Handlers that just display a page (i.e., retrieve data from the database
and stuff it into a template).</li>
<li>Handlers that process form submissions.</li>
</ul>
<div class="section" id="handlers-that-only-display-a-page">
<h4><a class="toc-backref" href="#id25">Handlers that Only Display a Page</a></h4>
<p><tt class="docutils literal"><span class="pre">ShowArticlesHandler</span></tt>, <tt class="docutils literal"><span class="pre">NewArticlesHandler</span></tt> and <tt class="docutils literal"><span class="pre">EditArticlesHandler</span></tt>
are example of handlers that simply display a page. Here's the
<tt class="docutils literal"><span class="pre">ShowArticlesHandler</span></tt> class again:</p>
<div class="code-block python">
<div class="highlight"><pre><span class="k">class</span> <span class="nc">ShowArticlesHandler</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">BlogRequestHandler</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">articles</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">get_all</span><span class="p">()</span>
        <span class="n">template_vars</span> <span class="o">=</span> <span class="p">{</span><span class="s">'articles'</span> <span class="p">:</span> <span class="n">articles</span><span class="p">}</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">render_template</span><span class="p">(</span><span class="s">'admin-main.html'</span><span class="p">,</span> <span class="n">template_vars</span><span class="p">)</span>
</pre></div>

</div>
<p>First, because it defines only the <tt class="docutils literal"><span class="pre">get()</span></tt> method, it supports just the
HTTP GET semantics. (POST is not supported for the associated URL.)</p>
<p>The actual handler is simple: It retrieves all articles, whether draft or
published, puts the resulting list in a dictionary, and uses that dictionary
to render the template. That's it; that's the <em>entire</em> handler. The
<tt class="docutils literal"><span class="pre">NewArticleHandler</span></tt> is similarly simple.</p>
<p>The <tt class="docutils literal"><span class="pre">EditArticleHandler</span></tt> is a little more complicated, only because it
has to handle a few additional things:</p>
<div class="code-block python">
<div class="highlight"><pre><span class="k">class</span> <span class="nc">EditArticleHandler</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">BlogRequestHandler</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="nb">id</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'id'</span><span class="p">))</span>
        <span class="n">article</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">article</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">,</span> <span class="s">'Article with ID </span><span class="si">%d</span><span class="s"> does not exist.'</span> <span class="o">%</span> <span class="nb">id</span>

        <span class="n">article</span><span class="o">.</span><span class="n">tag_string</span> <span class="o">=</span> <span class="s">', '</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">article</span><span class="o">.</span><span class="n">tags</span><span class="p">)</span>
        <span class="n">template_vars</span> <span class="o">=</span> <span class="p">{</span><span class="s">'article'</span>  <span class="p">:</span> <span class="n">article</span><span class="p">}</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">render_template</span><span class="p">(</span><span class="s">'admin-edit.html'</span><span class="p">,</span> <span class="n">template_vars</span><span class="p">)</span>
</pre></div>

</div>
<p>First, it determines whether the article being edited is in the database or
not; if not, it throws an exception, because it should never be invoked on
a non-existent article. (If it is, we have a bug.)</p>
<p>Next, it creates a comma-separated string from the list of tags, so the
template can simply stuff that string into the tags edit box.</p>
</div>
<div class="section" id="handlers-that-process-forms">
<h4><a class="toc-backref" href="#id26">Handlers that Process Forms</a></h4>
<p>The most complicated handler is the <tt class="docutils literal"><span class="pre">SaveArticleHandler</span></tt> class. Let's
look at that one again:</p>
<div class="code-block python">
<div class="highlight"><pre><span class="k">class</span> <span class="nc">SaveArticleHandler</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">BlogRequestHandler</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">post</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">title</span> <span class="o">=</span> <span class="n">cgi</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'title'</span><span class="p">))</span>
        <span class="n">body</span> <span class="o">=</span> <span class="n">cgi</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'content'</span><span class="p">))</span>
        <span class="nb">id</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">cgi</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'id'</span><span class="p">)))</span>
        <span class="n">tags</span> <span class="o">=</span> <span class="n">cgi</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'tags'</span><span class="p">))</span>
        <span class="n">timestamp</span> <span class="o">=</span> <span class="n">cgi</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'timestamp'</span><span class="p">))</span>
        <span class="n">draft</span> <span class="o">=</span> <span class="n">cgi</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'draft'</span><span class="p">))</span>
        <span class="k">if</span> <span class="n">tags</span><span class="p">:</span>
            <span class="n">tags</span> <span class="o">=</span> <span class="p">[</span><span class="n">t</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">tags</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s">','</span><span class="p">)]</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">tags</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="n">tags</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">convert_string_tags</span><span class="p">(</span><span class="n">tags</span><span class="p">)</span>

        <span class="k">if</span> <span class="ow">not</span> <span class="n">draft</span><span class="p">:</span>
            <span class="n">draft</span> <span class="o">=</span> <span class="bp">False</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">draft</span> <span class="o">=</span> <span class="p">(</span><span class="n">draft</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="o">==</span> <span class="s">'on'</span><span class="p">)</span>

        <span class="n">article</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">article</span><span class="p">:</span>
            <span class="c"># It's an edit of an existing item.</span>
            <span class="n">article</span><span class="o">.</span><span class="n">title</span> <span class="o">=</span> <span class="n">title</span>
            <span class="n">article</span><span class="o">.</span><span class="n">body</span> <span class="o">=</span> <span class="n">body</span>
            <span class="n">article</span><span class="o">.</span><span class="n">tags</span> <span class="o">=</span> <span class="n">tags</span>
            <span class="n">article</span><span class="o">.</span><span class="n">draft</span> <span class="o">=</span> <span class="n">draft</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="c"># It's new.</span>
            <span class="n">article</span> <span class="o">=</span> <span class="n">Article</span><span class="p">(</span><span class="n">title</span><span class="o">=</span><span class="n">title</span><span class="p">,</span>
                              <span class="n">body</span><span class="o">=</span><span class="n">body</span><span class="p">,</span>
                              <span class="n">tags</span><span class="o">=</span><span class="n">tags</span><span class="p">,</span>
                              <span class="nb">id</span><span class="o">=</span><span class="nb">id</span><span class="p">,</span>
                              <span class="n">draft</span><span class="o">=</span><span class="n">draft</span><span class="p">)</span>

        <span class="n">article</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>

        <span class="n">edit_again</span> <span class="o">=</span> <span class="n">cgi</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'edit_again'</span><span class="p">))</span>
        <span class="n">edit_again</span> <span class="o">=</span> <span class="n">edit_again</span> <span class="ow">and</span> <span class="p">(</span><span class="n">edit_again</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="o">==</span> <span class="s">'true'</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">edit_again</span><span class="p">:</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">redirect</span><span class="p">(</span><span class="s">'/admin/article/edit/?id=</span><span class="si">%s</span><span class="s">'</span> <span class="o">%</span> <span class="nb">id</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">redirect</span><span class="p">(</span><span class="s">'/admin/'</span><span class="p">)</span>
</pre></div>

</div>
<p>That one's a little longer, but what it does is simple enough:</p>
<ul class="simple">
<li>First, it retrieves all the form variables.</li>
<li>Next, if there are any tags in the form, it splits the tag string
to make it into a list.</li>
<li>The tags are actually stored as GAE <tt class="docutils literal"><span class="pre">db.Category</span></tt> objects, not strings,
so the code calls a special <tt class="docutils literal"><span class="pre">Article</span></tt> class method to convert the
strings from the form into <tt class="docutils literal"><span class="pre">Category</span></tt> objects. (Consult the code for
that conversion method; it's trivial, and it's not included here.)</li>
<li>It processes the Draft checkbox.</li>
<li>It attempts to load the referenced article. If the article exists,
the handler updates its contents. Otherwise, it creates a new Article
object with the specified ID.</li>
<li>Then, it saves the article.</li>
<li>If the <tt class="docutils literal"><span class="pre">edit_again</span></tt> request variable is set, then the handler redisplays
the edit screen; otherwise, it displays the main administration screen
again.</li>
</ul>
<p>That's it. We've finished our admin screens. Let's take a look at them. To
do that, fire up a terminal window, change your working directory to the
<tt class="docutils literal"><span class="pre">picoblog</span></tt> directory, and run the following command. (You must have put
the root of the unpacked GAE toolkit in your path.)</p>
<div class="code-block bash">
<div class="highlight"><pre>dev_appserver.py .
</pre></div>

</div>
<p>You'll see output something like this:</p>
<div class="code-block text">
<div class="highlight"><pre>INFO     2008-08-06 02:51:26,336 appcfg.py] Server: appengine.google.com
INFO     2008-08-06 02:51:26,342 appcfg.py] Checking for updates to the SDK.
INFO     2008-08-06 02:51:26,444 appcfg.py] The SDK is up to date.
INFO     2008-08-06 02:51:26,534 dev_appserver_main.py] Running application
pico on port 8080: http://localhost:8080
</pre></div>

</div>
<p>You can now surf to <tt class="docutils literal"><span class="pre">http://localhost:8080/admin/</span></tt> using your browser.</p>
<p>Here's a screen shot of the main screen, showing several articles. The
top-most article is a draft; the rest are published.</p>
<img alt="/static/gae/admin-main-small.png" src="Writing%20Blogging%20Software%20for%20GAE_files/admin-main-small.png" style="width: 387px; height: 383px;">
<p>The <a class="reference external" href="http://brizzled.clapper.org./static/gae/admin-main.png">full-size main page image is here</a>.</p>
<p>And here's the edit screen for the draft article:</p>
<img alt="/static/gae/admin-edit-small.png" src="Writing%20Blogging%20Software%20for%20GAE_files/admin-edit-small.png" style="width: 387px; height: 386px;">
<p>The <a class="reference external" href="http://brizzled.clapper.org./static/gae/admin-edit.png">full-size edit page image is here</a>.</p>
<p>From a stylistic viewpoint, these screens are really simple. However,
making them look fancier and slicker is simply a matter of fiddling with
the templates and the stylesheet. The Python code doesn't change.</p>
</div>
</div>
</div>
</div>
<div class="section" id="the-markup-language">
<h1><a class="toc-backref" href="#id27">The Markup Language</a></h1>
<p>Rather than force the blogger (i.e., you or me) to enter HTML, I've chosen
to use the <a class="reference external" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> (RST) markup language. Of course, this means
the blog has to translate the RST text into HTML when someone wants to view
the blog. We can either do that conversion when we save the article, or
convert on the fly when someone visits the blog.</p>
<p>Converting the markup when we save the article is more efficient, but it
means we have to store the generated HTML and reconvert all previously
saved articles whenever we change the templates or the style sheet. It's
simpler to convert on the fly. If this strategy ends up causing a
performance problem, we can always go back later and add page caching.</p>
<div class="section" id="docutils">
<h2><a class="toc-backref" href="#id28">Docutils</a></h2>
<p>To support RST, the first thing we have to do is make the <a class="reference external" href="http://docutils.sourceforge.net/">Docutils</a> package
available to our running code. The easiest way to do that is to visit the
<a class="reference external" href="http://docutils.sourceforge.net/">Docutils</a> web site, download the source code, unpack it, and move the
<tt class="docutils literal"><span class="pre">docutils</span></tt> subdirectory (and all its contents) into our blog directory.
When we later upload the application to GAE, the Docutils code will get
uploaded, too.</p>
<p>Docutils also looks for a <tt class="docutils literal"><span class="pre">roman.py</span></tt> file, which isn't present in the
GAE Python environment. There's one in the Google App Engine source directory
(which you downloaded); copy the <tt class="docutils literal"><span class="pre">roman.py</span></tt> file from there into the
top directory of the blog.</p>
</div>
<div class="section" id="translation-code">
<h2><a class="toc-backref" href="#id29">Translation code</a></h2>
<p>The code that actually translates RST to HTML is rather simple:</p>
<div class="code-block python">
<div class="highlight"><pre><span class="k">import</span> <span class="nn">os</span>

<span class="k">from</span> <span class="nn">docutils.core</span> <span class="k">import</span> <span class="n">publish_parts</span>

<span class="k">def</span> <span class="nf">rst2html</span><span class="p">(</span><span class="n">s</span><span class="p">):</span>
    <span class="n">settings</span> <span class="o">=</span> <span class="p">{</span><span class="s">'config'</span> <span class="p">:</span> <span class="bp">None</span><span class="p">}</span>

    <span class="c"># Necessary, because otherwise docutils attempts to read a config file</span>
    <span class="c"># via the codecs module, which doesn't work with AppEngine.</span>
    <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="s">'DOCUTILSCONFIG'</span><span class="p">]</span> <span class="o">=</span> <span class="s">""</span>
    <span class="n">parts</span> <span class="o">=</span> <span class="n">publish_parts</span><span class="p">(</span><span class="n">source</span><span class="o">=</span><span class="n">s</span><span class="p">,</span>
                          <span class="n">writer_name</span><span class="o">=</span><span class="s">'html4css1'</span><span class="p">,</span>
                          <span class="n">settings_overrides</span><span class="o">=</span><span class="n">settings</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">parts</span><span class="p">[</span><span class="s">'fragment'</span><span class="p">]</span>
</pre></div>

</div>
<p>The only wrinkle is the setting of the <tt class="docutils literal"><span class="pre">DOCUTILSCONFIG</span></tt> environment variable.
I determined empirically that if you don't set that variable to an empty string,
the Docutils package attempts to read a startup file via the <tt class="docutils literal"><span class="pre">codecs</span></tt>
module, and the way it calls the <tt class="docutils literal"><span class="pre">codecs.open()</span></tt> method conflicts with
how that method is defined in the GAE Python library. (GAE has replaced
Python's file handling routines with routines of its own, and they're not
always 100% compatible.)</p>
<p>Store this code in file <tt class="docutils literal"><span class="pre">rst.py</span></tt>. We'll then import it in our display code.</p>
</div>
</div>
<div class="section" id="create-the-display-screens">
<h1><a class="toc-backref" href="#id30">Create the Display Screens</a></h1>
<p>Now we're ready to create the display screens. There are six views to
support:</p>
<ul>
<li><p class="first"><strong>Main</strong> shows the top <em>n</em> articles (where <em>n</em> is the value of
<tt class="docutils literal"><span class="pre">MAX_ARTICLES_PER_PAGE</span></tt> in the <tt class="docutils literal"><span class="pre">defs.py</span></tt> file). This screen is the
main blog screen--the one a visitor sees first.</p>
</li>
<li><p class="first"><strong>Show One Article</strong> shows a single article. It's used when someone clicks</p>
<p>on the link for a single article.</p>
</li>
<li><p class="first"><strong>Show Articles by Tag</strong> shows all articles with a specific tag.</p>
</li>
<li><p class="first"><strong>Show Articles by Month</strong> shows all articles in a specified month.</p>
</li>
<li><p class="first"><strong>Show Archive</strong> lists the titles and dates of all articles in the blog.</p>
</li>
<li><p class="first"><strong>Not Found</strong> is a simple screen to display when an article or page
isn't found.</p>
</li>
</ul>
<p>We'll also add some query methods to the <tt class="docutils literal"><span class="pre">Article</span></tt> class as we go along.</p>
<div class="section" id="base-template">
<h2><a class="toc-backref" href="#id31">Base Template</a></h2>
<p>The simplest way to build these screens is to use Django template inheritance,
which has the additional benefit of ensuring a consistent look. Most of the
HTML goes into a <a class="reference external" href="http://brizzled.clapper.org./static/gae/base.txt">base template</a>. That template defines the basic look and
feel of the display pages, with various template substitutions like
<tt class="docutils literal"><span class="pre">{{</span> <span class="pre">blog_name}}</span></tt> and <tt class="docutils literal"><span class="pre">{{</span> <span class="pre">blog_owner}}</span></tt>.</p>
<p>However, the base template also contains template code like the following:</p>
<div class="code-block html">
<div class="highlight"><pre>&lt;div id="articles_container"&gt;
  {% block main %}{% endblock %}
&lt;/div&gt;
</pre></div>

</div>
<p>and this:</p>
<div class="code-block html">
<div class="highlight"><pre>&lt;div id="right-margin"&gt;
  {% block recent_list %}{% endblock %}
  {% block date_list %}{% endblock %}
&lt;/div&gt;
</pre></div>

</div>
<p>The blocks can be filled in by other templates that inherit from this one.</p>
</div>
<div class="section" id="blog-py">
<h2><a class="toc-backref" href="#id32"><tt class="docutils literal"><span class="pre">blog.py</span></tt></a></h2>
<p>The handlers go in <tt class="docutils literal"><span class="pre">blog.py</span></tt>, which is similar to <tt class="docutils literal"><span class="pre">admin.py</span></tt>. There's
an initialization section at the bottom that sets up the URL-to-handler
mappings. Let look at that first:</p>
<div class="code-block python">
<div class="highlight"><pre><span class="n">application</span> <span class="o">=</span> <span class="n">webapp</span><span class="o">.</span><span class="n">WSGIApplication</span><span class="p">(</span>
    <span class="p">[(</span><span class="s">'/'</span><span class="p">,</span> <span class="n">FrontPageHandler</span><span class="p">),</span>
     <span class="p">(</span><span class="s">'/tag/([^/]+)/*$'</span><span class="p">,</span> <span class="n">ArticlesByTagHandler</span><span class="p">),</span>
     <span class="p">(</span><span class="s">'/date/(\d\d\d\d)-(\d\d)/?$'</span><span class="p">,</span> <span class="n">ArticlesForMonthHandler</span><span class="p">),</span>
     <span class="p">(</span><span class="s">'/id/(\d+)/?$'</span><span class="p">,</span> <span class="n">SingleArticleHandler</span><span class="p">),</span>
     <span class="p">(</span><span class="s">'/archive/?$'</span><span class="p">,</span> <span class="n">ArchivePageHandler</span><span class="p">),</span>
     <span class="p">(</span><span class="s">'/rss2/?$'</span><span class="p">,</span> <span class="n">RSSFeedHandler</span><span class="p">),</span>
     <span class="p">(</span><span class="s">'/atom/?$'</span><span class="p">,</span> <span class="n">AtomFeedHandler</span><span class="p">),</span>
     <span class="p">(</span><span class="s">'/.*$'</span><span class="p">,</span> <span class="n">NotFoundPageHandler</span><span class="p">),</span>
     <span class="p">],</span>

    <span class="n">debug</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
</pre></div>

</div>
</div>
<div class="section" id="abstractpagehandler">
<h2><a class="toc-backref" href="#id33"><tt class="docutils literal"><span class="pre">AbstractPageHandler</span></tt></a></h2>
<p>At the top of the file, there's a base class that consolidates a lot of
the common logic. The most important method it contains is
<tt class="docutils literal"><span class="pre">render_articles()</span></tt>:</p>
<div class="code-block python">
<div class="highlight"><pre><span class="k">class</span> <span class="nc">AbstractPageHandler</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">BlogRequestHandler</span><span class="p">):</span>

    <span class="k">def</span> <span class="nf">render_articles</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span>
                        <span class="n">articles</span><span class="p">,</span>
                        <span class="n">request</span><span class="p">,</span>
                        <span class="n">recent</span><span class="p">,</span>
                        <span class="n">template_name</span><span class="o">=</span><span class="s">'show-articles.html'</span><span class="p">):</span>
        <span class="n">url_prefix</span> <span class="o">=</span> <span class="s">'http://'</span> <span class="o">+</span> <span class="n">request</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="s">'SERVER_NAME'</span><span class="p">]</span>
        <span class="n">port</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="s">'SERVER_PORT'</span><span class="p">]</span>
        <span class="k">if</span> <span class="n">port</span><span class="p">:</span>
            <span class="n">url_prefix</span> <span class="o">+=</span> <span class="s">':</span><span class="si">%s</span><span class="s">'</span> <span class="o">%</span> <span class="n">port</span>

        <span class="bp">self</span><span class="o">.</span><span class="n">augment_articles</span><span class="p">(</span><span class="n">articles</span><span class="p">,</span> <span class="n">url_prefix</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">augment_articles</span><span class="p">(</span><span class="n">recent</span><span class="p">,</span> <span class="n">url_prefix</span><span class="p">)</span>

        <span class="n">last_updated</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span>
        <span class="k">if</span> <span class="n">articles</span><span class="p">:</span>
            <span class="n">last_updated</span> <span class="o">=</span> <span class="n">articles</span><span class="p">[</span><span class="mf">0</span><span class="p">]</span><span class="o">.</span><span class="n">published_when</span>

        <span class="bp">self</span><span class="o">.</span><span class="n">adjust_timestamps</span><span class="p">(</span><span class="n">articles</span><span class="p">)</span>
        <span class="n">last_updated</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">adjust_timestamp</span><span class="p">(</span><span class="n">last_updated</span><span class="p">)</span>

        <span class="n">blog_url</span> <span class="o">=</span> <span class="n">url_prefix</span>
        <span class="n">tag_path</span> <span class="o">=</span> <span class="s">'/'</span> <span class="o">+</span> <span class="n">defs</span><span class="o">.</span><span class="n">TAG_URL_PATH</span>
        <span class="n">tag_url</span> <span class="o">=</span> <span class="n">url_prefix</span> <span class="o">+</span> <span class="n">tag_path</span>
        <span class="n">date_path</span> <span class="o">=</span> <span class="s">'/'</span> <span class="o">+</span> <span class="n">defs</span><span class="o">.</span><span class="n">DATE_URL_PATH</span>
        <span class="n">date_url</span> <span class="o">=</span> <span class="n">url_prefix</span> <span class="o">+</span> <span class="n">date_path</span>
        <span class="n">media_path</span> <span class="o">=</span> <span class="s">'/'</span> <span class="o">+</span> <span class="n">defs</span><span class="o">.</span><span class="n">MEDIA_URL_PATH</span>
        <span class="n">media_url</span> <span class="o">=</span> <span class="n">url_prefix</span> <span class="o">+</span> <span class="n">media_path</span>

        <span class="n">template_variables</span> <span class="o">=</span> <span class="p">{</span><span class="s">'blog_name'</span>    <span class="p">:</span> <span class="n">defs</span><span class="o">.</span><span class="n">BLOG_NAME</span><span class="p">,</span>
                              <span class="s">'blog_owner'</span>   <span class="p">:</span> <span class="n">defs</span><span class="o">.</span><span class="n">BLOG_OWNER</span><span class="p">,</span>
                              <span class="s">'articles'</span>     <span class="p">:</span> <span class="n">articles</span><span class="p">,</span>
                              <span class="s">'tag_list'</span>     <span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_tag_counts</span><span class="p">(),</span>
                              <span class="s">'date_list'</span>    <span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_month_counts</span><span class="p">(),</span>
                              <span class="s">'version'</span>      <span class="p">:</span> <span class="s">'0.3'</span><span class="p">,</span>
                              <span class="s">'last_updated'</span> <span class="p">:</span> <span class="n">last_updated</span><span class="p">,</span>
                              <span class="s">'blog_path'</span>    <span class="p">:</span> <span class="s">'/'</span><span class="p">,</span>
                              <span class="s">'blog_url'</span>     <span class="p">:</span> <span class="n">blog_url</span><span class="p">,</span>
                              <span class="s">'archive_path'</span> <span class="p">:</span> <span class="s">'/'</span> <span class="o">+</span> <span class="n">defs</span><span class="o">.</span><span class="n">ARCHIVE_URL_PATH</span><span class="p">,</span>
                              <span class="s">'tag_path'</span>     <span class="p">:</span> <span class="n">tag_path</span><span class="p">,</span>
                              <span class="s">'tag_url'</span>      <span class="p">:</span> <span class="n">tag_url</span><span class="p">,</span>
                              <span class="s">'date_path'</span>    <span class="p">:</span> <span class="n">date_path</span><span class="p">,</span>
                              <span class="s">'date_url'</span>     <span class="p">:</span> <span class="n">date_url</span><span class="p">,</span>
                              <span class="s">'atom_path'</span>    <span class="p">:</span> <span class="s">'/'</span> <span class="o">+</span> <span class="n">defs</span><span class="o">.</span><span class="n">ATOM_URL_PATH</span><span class="p">,</span>
                              <span class="s">'rss2_path'</span>    <span class="p">:</span> <span class="s">'/'</span> <span class="o">+</span> <span class="n">defs</span><span class="o">.</span><span class="n">RSS2_URL_PATH</span><span class="p">,</span>
                              <span class="s">'media_path'</span>   <span class="p">:</span> <span class="n">media_path</span><span class="p">,</span>
                              <span class="s">'media_url'</span>    <span class="p">:</span> <span class="n">media_url</span><span class="p">,</span>
                              <span class="s">'recent'</span>       <span class="p">:</span> <span class="n">recent</span><span class="p">}</span>

        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">render_template</span><span class="p">(</span><span class="n">template_name</span><span class="p">,</span> <span class="n">template_variables</span><span class="p">)</span>
</pre></div>

</div>
<p>This method takes:</p>
<ul class="simple">
<li>a list of <tt class="docutils literal"><span class="pre">Article</span></tt> objects to be displayed</li>
<li>the original incoming HTTP request</li>
<li>a list of recent articles to display (which can be empty)</li>
<li>the template name to use, which defaults to the <tt class="docutils literal"><span class="pre">show-articles.html</span></tt>
template</li>
</ul>
<p><tt class="docutils literal"><span class="pre">render_articles()</span></tt> then puts together the list of template variables,
renders the specified template, and returns the result.</p>
<p>All the display handlers will use this method, which is why it resides in
the base class.</p>
<p>Another method we should examine is <tt class="docutils literal"><span class="pre">augment_articles()</span></tt>, also in the
<tt class="docutils literal"><span class="pre">AbstractPageHandler</span></tt> class:</p>
<div class="code-block python">
<div class="highlight"><pre><span class="k">def</span> <span class="nf">augment_articles</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">articles</span><span class="p">,</span> <span class="n">url_prefix</span><span class="p">,</span> <span class="n">html</span><span class="o">=</span><span class="bp">True</span><span class="p">):</span>
    <span class="k">for</span> <span class="n">article</span> <span class="ow">in</span> <span class="n">articles</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">html</span><span class="p">:</span>
            <span class="k">try</span><span class="p">:</span>
                <span class="n">article</span><span class="o">.</span><span class="n">html</span> <span class="o">=</span> <span class="n">rst2html</span><span class="p">(</span><span class="n">article</span><span class="o">.</span><span class="n">body</span><span class="p">)</span>
            <span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
                <span class="n">article</span><span class="o">.</span><span class="n">html</span> <span class="o">=</span> <span class="s">''</span>
        <span class="n">article</span><span class="o">.</span><span class="n">path</span> <span class="o">=</span> <span class="s">'/'</span> <span class="o">+</span> <span class="n">defs</span><span class="o">.</span><span class="n">ARTICLE_URL_PATH</span> <span class="o">+</span> <span class="s">'/</span><span class="si">%s</span><span class="s">'</span> <span class="o">%</span> <span class="n">article</span><span class="o">.</span><span class="n">id</span>
        <span class="n">article</span><span class="o">.</span><span class="n">url</span> <span class="o">=</span> <span class="n">url_prefix</span> <span class="o">+</span> <span class="n">article</span><span class="o">.</span><span class="n">path</span>
</pre></div>

</div>
<p>This method renders the HTML for each article to be displayed (if
requested), and computes the article's path and URL.</p>
<p>The base class also contains a few other methods used by <tt class="docutils literal"><span class="pre">render_template()</span></tt>:</p>
<ul class="simple">
<li><tt class="docutils literal"><span class="pre">get_tag_counts()</span></tt> assembles the list of unique tags, associating
an article count with each one. It also determines which CSS class to
associate with each tag, based on the tag's relative frequency, for use
when rendering the tag cloud; this information is returned in a list
of <tt class="docutils literal"><span class="pre">TagCount</span></tt> objects. (<tt class="docutils literal"><span class="pre">TagCount</span></tt> is defined in <tt class="docutils literal"><span class="pre">blog.py</span></tt>. It's
not shown here.)</li>
<li><tt class="docutils literal"><span class="pre">get_month_counts()</span></tt> returns a list of <tt class="docutils literal"><span class="pre">DateCount</span></tt> objects that
the number of articles in each unique month/year.</li>
<li><tt class="docutils literal"><span class="pre">get_recent()</span></tt> gets the most recent articles, making sure the list
doesn't exceed the maximum specified in <tt class="docutils literal"><span class="pre">defs.TOTAL_RECENT</span></tt>.</li>
</ul>
<p>(See the complete file in the <a class="reference external" href="http://www.clapper.org/software/python/picoblog/">source code</a> for details.)</p>
</div>
<div class="section" id="not-found-page">
<h2><a class="toc-backref" href="#id34">Not Found Page</a></h2>
<p>Next, let's get the <em>Not Found</em> page out of the way. The template is
very simple:</p>
<div class="code-block html">
<div class="highlight"><pre>{% extends "base.html" %}

{% block main %}
  &lt;p class="article_title"&gt;Not Found&lt;/p&gt;
  &lt;p&gt;Sorry, but there's no such page here.&lt;/p&gt;
{% endblock %}
</pre></div>

</div>
<p>It extends the base template and fills in the <tt class="docutils literal"><span class="pre">main</span></tt> block with a simple
static message. We'll use this template in a couple places.</p>
<p>The <tt class="docutils literal"><span class="pre">NotFoundHandler</span></tt> class is also simple:</p>
<div class="code-block python">
<div class="highlight"><pre><span class="k">class</span> <span class="nc">NotFoundPageHandler</span><span class="p">(</span><span class="n">AbstractPageHandler</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">response</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">render_articles</span><span class="p">([],</span>
                                                     <span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="p">,</span>
                                                     <span class="p">[],</span>
                                                     <span class="s">'not-found.html'</span><span class="p">))</span>
</pre></div>

</div>
<p>Recall that this handler is the last, catch-all handler in the list of
URLs in <tt class="docutils literal"><span class="pre">blog.py</span></tt>, so it's automatically invoked if the incoming request
doesn't match any of the preceding URLs.</p>
<p>That's all we have to do to install a custom "not found" handler.</p>
</div>
<div class="section" id="main-page">
<h2><a class="toc-backref" href="#id35">Main Page</a></h2>
<p>The main screen requires a template and a handler. With the base template
and the <tt class="docutils literal"><span class="pre">AbstractPageHandler</span></tt> class in place, both are pretty simple.
Here's the template, which resides in <tt class="docutils literal"><span class="pre">show-articles.html</span></tt>:</p>
<div class="code-block html">
<div class="highlight"><pre>{% extends "base.html" %}

{% block main %}
  {% for article in articles %}
    {% include "article.html" %}
    &lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
  {% endfor %}
{% endblock %}

{% block recent_list %}
  {% if recent %}
    &lt;b&gt;Recent:&lt;/b&gt;
    &lt;ul&gt;
    {% for article in recent %}
      &lt;li&gt;&lt;a href="{{ article.path }}"&gt;{{ article.title }}&lt;/a&gt;
    {% endfor %}
    &lt;/ul&gt;
  {% endif %}
{% endblock %}

{% block date_list %}
  {% if date_list %}
    &lt;b&gt;By month:&lt;/b&gt;
    &lt;ul&gt;
    {% for date_count in date_list %}
      &lt;li&gt;&lt;a href="{{ date_path }}/{{ date_count.date|date:"Y-m" }}/"&gt;
          {{ date_count.date|date:"F, Y" }}&lt;/a&gt; ({{ date_count.count }})
    {% endfor %}
    &lt;/ul&gt;
  {% endif %}
{% endblock %}

{% block tag_list %}
  {% if tag_list %}
    &lt;div id="tag-cloud"&gt;
    {% for tag_count in tag_list %}
      &lt;a class="{{ tag_count.css_class }}"
         href="{{ tag_path }}/{{ tag_count.tag }}/"&gt;
         {{ tag_count.tag }}({{ tag_count.count }})&lt;/a&gt;
         {% if not forloop.last %},{% endif %}
    {% endfor %}
    &lt;/div&gt;
  {% endif %}
{% endblock %}
</pre></div>

</div>
<p>The template extends the base template, and then just fills in the HTML for
each block that's defined in the base template. Note, in particular, this
block:</p>
<div class="code-block text">
<div class="highlight"><pre>{% for article in articles %}
  {% include "article.html" %}
{% endfor %}
</pre></div>

</div>
<p>The actual template that displays an article resides in yet another file,
so it can be re-used in different templates. <tt class="docutils literal"><span class="pre">show-articles.html</span></tt>
includes it, repeatedly, in a loop that traverses the list of articles to
be displayed.</p>
<p>The <tt class="docutils literal"><span class="pre">article.html</span></tt> template looks like this:</p>
<div class="code-block html">
<div class="highlight"><pre>&lt;table width="100%" border="0" cellspacing="0" cellpadding="0"&gt;
  &lt;tr class="article_title_line"&gt;
    &lt;td class="article_title"&gt;{{ article.title }}&lt;/td&gt;
    &lt;td class="timestamp"&gt;
       {{ article.published_when|date:"j F, Y \a\t g:i A" }}
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    {% if article.draft %}
    &lt;td colspan="2" class="article-body-draft"&gt;
    {% else %}
    &lt;td colspan="2" class="article-body"&gt;
    {% endif %}
    {{ article.html }}
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td colspan="2" class="article-footer"&gt;
      &lt;a href="{{ article.path }}" class="reference"&gt;Permalink&lt;/a&gt;|
      Tags: {{ article.tags|join:", " }}&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
</pre></div>

</div>
<p>It's relatively easy to understand: It assumes the existence of a variable
called <tt class="docutils literal"><span class="pre">article</span></tt> that contains the article to be displayed.</p>
<p>The handler for the main page is even simpler:</p>
<div class="code-block python">
<div class="highlight"><pre><span class="k">class</span> <span class="nc">FrontPageHandler</span><span class="p">(</span><span class="n">AbstractPageHandler</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">articles</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">published</span><span class="p">()</span>
        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">articles</span><span class="p">)</span> <span class="n">&gt;</span> <span class="n">defs</span><span class="o">.</span><span class="n">MAX_ARTICLES_PER_PAGE</span><span class="p">:</span>
            <span class="n">articles</span> <span class="o">=</span> <span class="n">articles</span><span class="p">[:</span><span class="n">defs</span><span class="o">.</span><span class="n">MAX_ARTICLES_PER_PAGE</span><span class="p">]</span>

        <span class="bp">self</span><span class="o">.</span><span class="n">response</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">render_articles</span><span class="p">(</span><span class="n">articles</span><span class="p">,</span>
                                                     <span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="p">,</span>
                                                     <span class="bp">self</span><span class="o">.</span><span class="n">get_recent</span><span class="p">()))</span>
</pre></div>

</div>
<p>It gets the list of published articles, trims it down to the maximum number
of articles on the main page, renders the articles to HTML, and dumps the
result to the App Engine HTTP response object.</p>
<p>If you did not leave the <tt class="docutils literal"><span class="pre">dev_appserver</span></tt> running, bring it up again.
Then, connect to <tt class="docutils literal"><span class="pre">http://localhost:8080/</span></tt>, and check out your main page.
It should look something like this:</p>
<img alt="/static/gae/main-small.png" src="Writing%20Blogging%20Software%20for%20GAE_files/main-small.png" style="width: 479px; height: 390px;">
<p>The <a class="reference external" href="http://brizzled.clapper.org./static/gae/main.png">full main page image is here</a>.</p>
</div>
<div class="section" id="show-one-article">
<h2><a class="toc-backref" href="#id36">Show One Article</a></h2>
<p>This screen shows a single article. It's invoked when a reader selects a
single post (e.g., <tt class="docutils literal"><span class="pre">http://www.example.org/blog/id/5</span></tt>).</p>
<p>It re-uses the same <tt class="docutils literal"><span class="pre">show-articles.html</span></tt> template, but with just one
article in the list. The handler looks like this:</p>
<pre class="literal-block">class SingleArticleHandler(AbstractPageHandler):

    def get(self, id):
        article = Article.get(int(id))
        if article:
            template = 'show-articles.html'
            articles = [article]
            more = None
        else:
            template = 'not-found.html'
            articles = []

        self.response.out.write(self.render_articles(articles=articles,
                                                     request=self.request,
                                                     recent=self.get_recent(),
                                                     template_name=template))
</pre>
<p>It attempts to retrieve the article with the specified ID. If the article
exists in the database, then the code puts it in a single-element list and
tells <tt class="docutils literal"><span class="pre">render_articles()</span></tt> to use the <tt class="docutils literal"><span class="pre">show_articles.html</span></tt> to display it.</p>
<p>If the article does not exist, the code uses the <tt class="docutils literal"><span class="pre">not-found.html</span></tt>
template we defined earlier to display the generic "not found" screen.</p>
<p>Note that this version of the <tt class="docutils literal"><span class="pre">get()</span></tt> method accepts an <tt class="docutils literal"><span class="pre">id</span></tt> parameter.
Where does that come from? Recall the configuration for this handler in
the <tt class="docutils literal"><span class="pre">WSGIApplication</span></tt> object at the bottom of the script:</p>
<div class="code-block python">
<div class="highlight"><pre><span class="n">application</span> <span class="o">=</span> <span class="n">webapp</span><span class="o">.</span><span class="n">WSGIApplication</span><span class="p">(</span>
    <span class="p">[</span> <span class="o">...</span>

     <span class="p">(</span><span class="s">'/id/(\d+)/?$'</span><span class="p">,</span> <span class="n">SingleArticleHandler</span><span class="p">),</span>

     <span class="o">...</span>
     <span class="p">],</span>
</pre></div>

</div>
<p>Note that the regular expression, <tt class="docutils literal"><span class="pre">'/id/(\d+)/?$</span></tt> contains a group,
<tt class="docutils literal"><span class="pre">(\d+)</span></tt>. Like Django, GAE maps each group into a parameter to the
<tt class="docutils literal"><span class="pre">get()</span></tt> or <tt class="docutils literal"><span class="pre">post()</span></tt> method. In this case, the string that matches the
regular expression group (the article's numeric ID, in this case) is passed
as the first parameter to the <tt class="docutils literal"><span class="pre">get()</span></tt> method.</p>
</div>
<div class="section" id="show-articles-by-tag">
<h2><a class="toc-backref" href="#id37">Show Articles By Tag</a></h2>
<p>The <tt class="docutils literal"><span class="pre">ArticlesByTagHandler</span></tt> class again re-uses the <tt class="docutils literal"><span class="pre">show-articles.html</span></tt>
template:</p>
<div class="code-block python">
<div class="highlight"><pre><span class="k">class</span> <span class="nc">ArticlesByTagHandler</span><span class="p">(</span><span class="n">AbstractPageHandler</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">tag</span><span class="p">):</span>
        <span class="n">articles</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">all_for_tag</span><span class="p">(</span><span class="n">tag</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">response</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">render_articles</span><span class="p">(</span><span class="n">articles</span><span class="p">,</span>
                                                     <span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="p">,</span>
                                                     <span class="bp">self</span><span class="o">.</span><span class="n">get_recent</span><span class="p">()))</span>
</pre></div>

</div>
<p>Note, however, that it's calling a class method called <tt class="docutils literal"><span class="pre">all_for_tag()</span></tt> in
the <tt class="docutils literal"><span class="pre">Article</span></tt> class. We have to extend <tt class="docutils literal"><span class="pre">Article</span></tt> to support this
query method. That method turns out to be trivial:</p>
<div class="code-block python">
<div class="highlight"><pre><span class="nd">@classmethod</span>
<span class="k">def</span> <span class="nf">all_for_tag</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="n">tag</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">Article</span><span class="o">.</span><span class="n">published_query</span><span class="p">()</span>\
                  <span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="s">'tags = '</span><span class="p">,</span> <span class="n">tag</span><span class="p">)</span>\
                  <span class="o">.</span><span class="n">order</span><span class="p">(</span><span class="s">'-published_when'</span><span class="p">)</span>\
                  <span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="n">FETCH_THEM_ALL</span><span class="p">)</span>
</pre></div>

</div>
<p>My original version of this method loaded all published articles and manually
searched through their tags. However, in an
<a class="reference external" href="http://groups.google.com/group/google-appengine/msg/a5fcf38345c54623">email to the Google App Engine mailing list, Bill Katz</a>
pointed me to <a class="reference external" href="http://code.google.com/appengine/docs/datastore/typesandpropertyclasses.html#ListProperty">something I missed in the GAE docs</a>:</p>
<blockquote>
In a query, comparing a list property to a value performs the test
against the list members: <tt class="docutils literal"><span class="pre">list_property</span> <span class="pre">=</span> <span class="pre">value</span></tt> tests if the value
appears anywhere in the list.</blockquote>
<p>This is convenient and more efficient than my original solution.</p>
</div>
<div class="section" id="show-articles-by-month">
<h2><a class="toc-backref" href="#id38">Show Articles By Month</a></h2>
<p>By now, you should be getting the hang of this.</p>
<p>Next, we have to write a handler that'll produce a page of posts for a
given month. As with the tag handler, the month handler is trivial:</p>
<div class="code-block python">
<div class="highlight"><pre><span class="k">class</span> <span class="nc">ArticlesForMonthHandler</span><span class="p">(</span><span class="n">AbstractPageHandler</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">year</span><span class="p">,</span> <span class="n">month</span><span class="p">):</span>
        <span class="n">articles</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">all_for_month</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">year</span><span class="p">),</span> <span class="nb">int</span><span class="p">(</span><span class="n">month</span><span class="p">))</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">response</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">render_articles</span><span class="p">(</span><span class="n">articles</span><span class="p">,</span>
                                                     <span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="p">,</span>
                                                     <span class="bp">self</span><span class="o">.</span><span class="n">get_recent</span><span class="p">()))</span>
</pre></div>

</div>
<p>Again, though, it calls an <tt class="docutils literal"><span class="pre">Article</span></tt> class method we have yet to write:</p>
<div class="code-block python">
<div class="highlight"><pre><span class="nd">@classmethod</span>
<span class="k">def</span> <span class="nf">all_for_month</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="n">year</span><span class="p">,</span> <span class="n">month</span><span class="p">):</span>
    <span class="n">start_date</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">date</span><span class="p">(</span><span class="n">year</span><span class="p">,</span> <span class="n">month</span><span class="p">,</span> <span class="mf">1</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">start_date</span><span class="o">.</span><span class="n">month</span> <span class="o">==</span> <span class="mf">12</span><span class="p">:</span>
        <span class="n">next_year</span> <span class="o">=</span> <span class="n">start_date</span><span class="o">.</span><span class="n">year</span> <span class="o">+</span> <span class="mf">1</span>
        <span class="n">next_month</span> <span class="o">=</span> <span class="mf">1</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">next_year</span> <span class="o">=</span> <span class="n">start_date</span><span class="o">.</span><span class="n">year</span>
        <span class="n">next_month</span> <span class="o">=</span> <span class="n">start_date</span><span class="o">.</span><span class="n">month</span> <span class="o">+</span> <span class="mf">1</span>

    <span class="n">end_date</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">date</span><span class="p">(</span><span class="n">next_year</span><span class="p">,</span> <span class="n">next_month</span><span class="p">,</span> <span class="mf">1</span><span class="p">)</span>
    <span class="n">query</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">published_query</span><span class="p">()</span>\
                   <span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="s">'published_when &gt;='</span><span class="p">,</span> <span class="n">start_date</span><span class="p">)</span>\
                   <span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="s">'published_when &lt;'</span><span class="p">,</span> <span class="n">end_date</span><span class="p">)</span>\
                   <span class="o">.</span><span class="n">order</span><span class="p">(</span><span class="s">'-published_when'</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">query</span><span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="n">FETCH_THEM_ALL</span><span class="p">)</span>
</pre></div>

</div>
<p>This method chains query filters to the query returned by
<tt class="docutils literal"><span class="pre">Article.published_query()</span></tt>. The filters ensure that the returned articles
are the ones published within the specified year and month.</p>
</div>
<div class="section" id="show-archive">
<h2><a class="toc-backref" href="#id39">Show Archive</a></h2>
<p>This page shows the titles of all published articles, in reverse
chronological order. I chose to make this page even simpler than the
other pages: It lacks the tag cloud, recent posts, and posts-by-month
sections in the margin. The template is trivial:</p>
<div class="code-block html">
<div class="highlight"><pre>{% extends "base.html" %}

{% block main %}
  &lt;span class="heading"&gt;Complete Archive:&lt;/span&gt;
  {% if articles %}
    &lt;ul&gt;
    {% for article in articles %}
      &lt;li&gt;&lt;a href="{{ article.path }}"&gt;{{ article.title }}&lt;/a&gt;
          ({{ article.timestamp|date:"j F, Y" }})
    {% endfor %}
    &lt;/ul&gt;
  {% else %}
  &lt;p&gt;This blog is empty. (Someone want to fix that?)
  {% endif %}
{% endblock %}
</pre></div>

</div>
<p>And the handler is, once again, trivial:</p>
<div class="code-block python">
<div class="highlight"><pre><span class="k">class</span> <span class="nc">ArchivePageHandler</span><span class="p">(</span><span class="n">AbstractPageHandler</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">articles</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">published</span><span class="p">()</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">response</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">render_articles</span><span class="p">(</span><span class="n">articles</span><span class="p">,</span>
                                                     <span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="p">,</span>
                                                     <span class="p">[],</span>
                                                     <span class="s">'archive.html'</span><span class="p">))</span>
</pre></div>

</div>
<p>Note that <tt class="docutils literal"><span class="pre">ArchivePageHandler</span></tt> passes an empty list for the "recent"
posts (since it won't be used) and the archive template.</p>
<p>Here's what the archive page looks like with our two articles in the
archive:</p>
<img alt="/static/gae/archive-small.png" src="Writing%20Blogging%20Software%20for%20GAE_files/archive-small.png" style="width: 480px; height: 213px;">
<p>The <a class="reference external" href="http://brizzled.clapper.org./static/gae/archive.png">full archive page image is here</a>.</p>
</div>
<div class="section" id="rss-feed">
<h2><a class="toc-backref" href="#id40">RSS Feed</a></h2>
<p>Any decent blog supplies an RSS feed, so we should do that, too. Of course,
that's simply a matter of writing a template and a small handler. By now, the
handler should look pretty familiar:</p>
<div class="code-block python">
<div class="highlight"><pre><span class="k">class</span> <span class="nc">RSSFeedHandler</span><span class="p">(</span><span class="n">AbstractPageHandler</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">articles</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">published</span><span class="p">()</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s">'Content-Type'</span><span class="p">]</span> <span class="o">=</span> <span class="s">'text/xml'</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">response</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">render_articles</span><span class="p">(</span><span class="n">articles</span><span class="p">,</span>
                                                     <span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="p">,</span>
                                                     <span class="p">[],</span>
                                                     <span class="s">'rss2.xml'</span><span class="p">))</span>
</pre></div>

</div>
<p>The template is simple, too:</p>
<div class="code-block xml">
<div class="highlight"><pre>&lt;?xml version="1.0" encoding="utf-8" ?&gt;
&lt;rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/"&gt;
  &lt;channel&gt;
    &lt;title&gt;{{ blog_name }}&lt;/title&gt;
    &lt;link&gt;{{ blog_url }}&lt;/link&gt;
    &lt;description&gt;{{ blog_name }}&lt;/description&gt;
    &lt;pubDate&gt;{{ last_updated|date:"D, d M Y H:i:s T" }}&lt;/pubDate&gt;
    {% for article in articles %}
    &lt;item&gt;
      &lt;title&gt;{{ article.title }}&lt;/title&gt;
      &lt;link&gt;{{ article.url }}&lt;/link&gt;
      &lt;guid&gt;{{ article.url }}&lt;/guid&gt;
      &lt;pubDate&gt;{{ article.timestamp|date:"D, d M Y H:i:s T" }}&lt;/pubDate&gt;
      &lt;description&gt;
        {{ article.html|escape }}
      &lt;/description&gt;
      &lt;author&gt;{{ blog_author }}&lt;/author&gt;
    &lt;/item&gt;
    {% endfor %}
  &lt;/channel&gt;
&lt;/rss&gt;
</pre></div>

</div>
</div>
</div>
<div class="section" id="deploy-the-application">
<h1><a class="toc-backref" href="#id41">Deploy the Application</a></h1>
<p>Test the application using <tt class="docutils literal"><span class="pre">dev_appserver</span></tt>; when you think it's ready,
it's time to deploy it. From within the blog's top-level source directory,
run this command:</p>
<div class="code-block bash">
<div class="highlight"><pre>appcfg.py update .
</pre></div>

</div>
<p>That'll upload your application to your Google App Engine account.
If the application name is <tt class="docutils literal"><span class="pre">picoblog</span></tt>, then your live application will
appear at <tt class="docutils literal"><span class="pre">http://picoblog.appspot.com/</span></tt>.</p>
</div>
<div class="section" id="handling-static-files-such-as-images">
<h1><a class="toc-backref" href="#id42">Handling Static Files such as Images</a></h1>
<p>Of course, a blog should be able to display images. Since our new blog
software doesn't support image upload, how can we use images? The answer is
simple, if slightly clunky: Put any images you want to use in your
<tt class="docutils literal"><span class="pre">picoblog/static</span></tt> directory. Then, use <tt class="docutils literal"><span class="pre">appcfg.py</span></tt> to update the
live application; <tt class="docutils literal"><span class="pre">appcfg.py</span></tt> will copy those images up to the Google App
Engine server, where you can use them.</p>
<p>For instance, assume you have a picture call <tt class="docutils literal"><span class="pre">foo.png</span></tt> that you want to
use in a blog article. Here's how you might deploy it:</p>
<div class="code-block bash">
<div class="highlight"><pre><span class="nv">$ </span><span class="nb">cd </span>picoblog
<span class="nv">$ </span>cp ~/foo.png static
<span class="nv">$ </span>appcfg.py update .
</pre></div>

</div>
<p>Then, you can use the reStructuredText <tt class="docutils literal"><span class="pre">..</span> <span class="pre">image</span></tt> directive to pull it
into an article:</p>
<div class="code-block rst">
<div class="highlight"><pre><span class="p">..</span> <span class="ow">image</span><span class="p">::</span> <span class="k">/static/foo.pn</span>
   <span class="nc">:width:</span> <span class="nf">180</span>
   <span class="nc">:height:</span> <span class="nf">150</span>
</pre></div>

</div>
</div>
<div class="section" id="previewing-a-draft">
<h1><a class="toc-backref" href="#id43">Previewing a Draft</a></h1>
<p>There's one last feature to add: The ability to preview a draft article
without publishing it.</p>
<p>With this software in place, you can already do that by using a separate
browser window or frame. For instance, suppose you're editing a new
article, and its ID happens to be 53. In another window, you can surf to
that ID directly, using the URL <tt class="docutils literal"><span class="pre">http://picoblog.appspot.com/id/53/</span></tt>.</p>
<p>But it might also be nice to preview the article in the same window where
you're doing your editing. That turns out to be trivial to implement:
Merely go back to <a class="reference internal" href="#the-edit-screen-template">The Edit Screen Template</a>, and add these lines right
after the end of the form:</p>
<div class="code-block html">
<div class="highlight"><pre>&lt;h1 class="admin-page-title"&gt;Preview:&lt;/h1&gt;
&lt;div style="border-top: 1px solid black"&gt;
&lt;iframe src="/id/{{ article.id }}" width="97%" scrolling="auto" height="750"
        frameborder="0"&gt;
&lt;/iframe&gt;
</pre></div>

</div>
<p>Now, you'll always have a preview frame underneath the edit controls.</p>
</div>
<div class="section" id="enhancements">
<h1><a class="toc-backref" href="#id44">Enhancements</a></h1>
<p>Now that you have the basic blog in place, you can start to add other
enhancements, such as:</p>
<ul class="simple">
<li>Support for <a class="reference external" href="http://pygments.org/">Pygments</a> syntax coloring.</li>
<li>Support for <a class="reference external" href="http://www.google.com/analytics/">Google Analytics</a>, which is useful for analyzing logs and
traffic.</li>
<li>Image uploading.</li>
<li>A more individual theme.</li>
<li>etc.</li>
</ul>
</div>
<div class="section" id="in-closing">
<h1><a class="toc-backref" href="#id45">In Closing</a></h1>
<p>In this (long) tutorial, we built a simple blog using Python and Google's
App Engine. The code represented in this article is very similar to the
code that runs this very blog; it's certainly effective, even if it lacks
certain bells and whistles right now. With any luck, you now have a
better understanding of what it means to build an application on App Engine.</p>
</div>
<div class="section" id="feedback">
<h1><a class="toc-backref" href="#id46">Feedback</a></h1>
<p>I welcome feedback, even though I don't accept comments. Feel free to
drop me an email with comments or corrections. I'll update this article
with any good stuff I receive.</p>
</div>
<div class="section" id="follow-up-articles">
<h1><a class="toc-backref" href="#id47">Follow-up Articles</a></h1>
<ul class="simple">
<li><a class="reference external" href="http://brizzled.clapper.org./id/78">Adding Page caching to a GAE application</a></li>
</ul>
</div>

      </td>
    </tr>
    <tr>
      <td colspan="2" class="article-footer">
        <a href="http://brizzled.clapper.org./id/77" class="reference">Permalink</a> | 
        <a href="http://brizzled.clapper.org./pr/77" class="reference">Printer-friendly</a> | 
        Tags: python, App Engine, blogging, software
      </td>
    </tr>
  </tbody></table>



</td></tr></tbody></table>

            </div>
          </td>
        </tr>
      </tbody></table>
     </td>

    <td width="20%">
      <div class="gutter">
        <table border="0">
          <tbody><tr>
            <td class="gutter-item">
              <div class="myname">Brian M. Clapper</div>
           </td>
          </tr>
          <tr>
            <td class="gutter-item">
              <img src="Writing%20Blogging%20Software%20for%20GAE_files/bmc-head-shot-2007-small.jpg" align="left" width="76" height="120">
            </td>
          </tr>
          <tr>
            <td class="gutter-item">
              <div class="email">bmc&nbsp;@&nbsp;clapper.org</div>
            </td>
          </tr>
          <tr>
            <td class="gutter-item">
              <a href="http://brizzled.clapper.org./atom" class="button"><img src="Writing%20Blogging%20Software%20for%20GAE_files/atom-small.gif" align="left" border="0" width="32" height="15"></a>
            </td>
          </tr>
          <tr>
            <td class="gutter-item">
              <a href="http://brizzled.clapper.org./rss2" class="button"><img src="Writing%20Blogging%20Software%20for%20GAE_files/rss2-small.gif" border="0" width="49" height="15"></a>
            </td>
          </tr>
        </tbody></table>
        <br>
        
  
    <span class="gutter-header">RECENT</span>
    <ul>
    
      <li><a href="http://brizzled.clapper.org./id/78">Adding Page caching to a GAE application</a>
    
      </li><li><a href="http://brizzled.clapper.org./id/77">Writing Blogging Software for Google App Engine</a>
    
      </li><li><a href="http://brizzled.clapper.org./id/76">Now blogging on App Engine</a>
    
      </li><li><a href="http://brizzled.clapper.org./id/75">Why is Python more fun than Java?</a>
    
      </li><li><a href="http://brizzled.clapper.org./id/74">Serving a website from S3</a>
    
      </li><li><a href="http://brizzled.clapper.org./id/73">Django blog</a>
    
      </li><li><a href="http://brizzled.clapper.org./id/72">And we're back</a>
    
      </li><li><a href="http://brizzled.clapper.org./id/4">Brizzled</a>
    
      </li><li><a href="http://brizzled.clapper.org./id/2">Python, sys.path and EasyInstall</a>
    
      </li><li><a href="http://brizzled.clapper.org./id/71">Gun Nuts</a>
    
    </li></ul>
  


        
  
    <span class="gutter-header">BY MONTH</span>
    <ul>
    
      <li><a href="http://brizzled.clapper.org./date/2008-08/">August, 2008</a> (3)
    
      </li><li><a href="http://brizzled.clapper.org./date/2008-07/">July, 2008</a> (5)
    
      </li><li><a href="http://brizzled.clapper.org./date/2008-03/">March, 2008</a> (1)
    
      </li><li><a href="http://brizzled.clapper.org./date/2007-08/">August, 2007</a> (2)
    
      </li><li><a href="http://brizzled.clapper.org./date/2007-07/">July, 2007</a> (3)
    
      </li><li><a href="http://brizzled.clapper.org./date/2007-06/">June, 2007</a> (2)
    
      </li><li><a href="http://brizzled.clapper.org./date/2007-04/">April, 2007</a> (1)
    
      </li><li><a href="http://brizzled.clapper.org./date/2007-02/">February, 2007</a> (1)
    
      </li><li><a href="http://brizzled.clapper.org./date/2006-04/">April, 2006</a> (1)
    
      </li><li><a href="http://brizzled.clapper.org./date/2006-03/">March, 2006</a> (4)
    
      </li><li><a href="http://brizzled.clapper.org./date/2006-02/">February, 2006</a> (1)
    
      </li><li><a href="http://brizzled.clapper.org./date/2006-01/">January, 2006</a> (3)
    
      </li><li><a href="http://brizzled.clapper.org./date/2005-12/">December, 2005</a> (3)
    
      </li><li><a href="http://brizzled.clapper.org./date/2005-10/">October, 2005</a> (2)
    
      </li><li><a href="http://brizzled.clapper.org./date/2005-09/">September, 2005</a> (1)
    
    </li></ul>
  


      </div>
    </td>
  </tr>

  <tr>
    <td class="margin-area" align="right" valign="bottom" width="80%">
        
  
    <div id="tag-cloud">
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/terrorism/">terrorism(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/vps/">vps(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/macabre/">macabre(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/homosexuality/">homosexuality(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/high-scalability/">high-scalability(1)</a>,
    
      <a class="tag-cloud-small" href="http://brizzled.clapper.org./tag/django/">django(2)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/spotlight/">spotlight(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/generalist/">generalist(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/idiocy/">idiocy(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/perfectionism/">perfectionism(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/java/">java(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/crime/">crime(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/semaphore/">semaphore(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/impeachment/">impeachment(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/surveillance/">surveillance(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/risks/">risks(1)</a>,
    
      <a class="tag-cloud-large" href="http://brizzled.clapper.org./tag/computers/">computers(5)</a>,
    
      <a class="tag-cloud-medium" href="http://brizzled.clapper.org./tag/politics/">politics(3)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/comments/">comments(1)</a>,
    
      <a class="tag-cloud-small" href="http://brizzled.clapper.org./tag/mac%20os%20x/">mac os x(2)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/religious%20right/">religious right(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/photo-with-santa/">photo-with-santa(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/health/">health(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/disney/">disney(1)</a>,
    
      <a class="tag-cloud-small" href="http://brizzled.clapper.org./tag/programming/">programming(2)</a>,
    
      <a class="tag-cloud-medium" href="http://brizzled.clapper.org./tag/spam/">spam(3)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/digital-photography/">digital-photography(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/zappa/">zappa(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/aladdin/">aladdin(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/organized%20labor/">organized labor(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/networking/">networking(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/george%20bush/">george bush(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/web/">web(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/Robert%20Negus/">Robert Negus(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/avian%20flu/">avian flu(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/guns/">guns(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/pen-tablet/">pen-tablet(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/lighting/">lighting(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/H5N1/">H5N1(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/brizzled/">brizzled(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/stylus/">stylus(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/WAP/">WAP(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/unions/">unions(1)</a>,
    
      <a class="tag-cloud-huge" href="http://brizzled.clapper.org./tag/people/">people(6)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/oracle/">oracle(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/phlx/">phlx(1)</a>,
    
      <a class="tag-cloud-large" href="http://brizzled.clapper.org./tag/blogging/">blogging(5)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/melanoma/">melanoma(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/WPA/">WPA(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/unix/">unix(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/frank%20zappa/">frank zappa(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/privacy/">privacy(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/innumeracy/">innumeracy(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/wacom-graphire/">wacom-graphire(1)</a>,
    
      <a class="tag-cloud-huge" href="http://brizzled.clapper.org./tag/python/">python(7)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/security/">security(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/microsoft/">microsoft(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/spotlightfs/">spotlightfs(1)</a>,
    
      <a class="tag-cloud-huge" href="http://brizzled.clapper.org./tag/software/">software(7)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/tablet/">tablet(1)</a>,
    
      <a class="tag-cloud-small" href="http://brizzled.clapper.org./tag/photography/">photography(2)</a>,
    
      <a class="tag-cloud-small" href="http://brizzled.clapper.org./tag/App%20Engine/">App Engine(2)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/barack%20obama/">barack obama(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/deadlock/">deadlock(1)</a>,
    
      <a class="tag-cloud-small" href="http://brizzled.clapper.org./tag/second%20life/">second life(2)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/AppEngine/">AppEngine(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/philadelphia/">philadelphia(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/wiretapping/">wiretapping(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/intolerance/">intolerance(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/photofloods/">photofloods(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/s3/">s3(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/polly%20pockets/">polly pockets(1)</a>,
    
      <a class="tag-cloud-tiny" href="http://brizzled.clapper.org./tag/chicken/">chicken(1)</a>
    
    </div>
  


    </td>
    <td class="margin-area" align="right" valign="top" width="20%">&nbsp;</td>
  </tr>

</tbody></table>

</body></html>